PHP JavaScript deferred Loading
Deferred loading is a programming technique to improve the performance of Websites by loading code only when needed. The purpose of this article is to show how to use deferred loading of Webpage parts using PHP and JavaScript in combination with AJAX and jQuery.
Deferred loading can also be used on CSS, but that is not the scope of this article.
Introduction
When a website loads, not all content is always necessary. So why should you load such content if you don't know the use is gonna use it? In this article is assumed that the website consist on (Bootstrap) navigation tabs. The user can have several roles that gives or denies him access to certain parts of the website. If the user has the admin-role, he may access everything including the admin tab. In that case you don't want to load the admin tab at all if the user hasn't the admin-role. But also if the user has the admin-role you do need the admin tab every time you access the website.
In that case you wanna use deferred loading.
Requirements
The framework we are going to make depends on PHP-server-coding and JavaScript-Client-coding. To clue the 2 parts we are using AJAX in combination with jQuery. The application loads a form with JavaScript validation and AJAX calls for checking and CRUD operations on the Database. The form has a Bootgrid part for showing the content of the database. Next the application is based on multiple PHP and JavaScript Generic methods for example:
- JS - logts()
- JS - sprintf()
- PHP - Log4php [1].
The implementation of the code has to be seen as an example. The code will NEVER run on your system without modifications and is meant as an example.
Embedded JavaScript
The JS files are all embedded in PHP Files, which enables the usage of PHP in JavaScript. This technique makes the integration of Frontend and Backend much easier and can also be used for Cascading Style Sheet (CSS-Files).
Multi-Language Support
The examples uses one of the many multi-language support methods (See all PHP-function _h()
).
Used files
FileName | Description |
---|---|
AdminHomeDb.php | Front- and Backend for constants (variable-names), Form creation, Database CRUD methods and Database CRUD checks. |
AppValidateJS.php | Frontend, generic validation and patterns, Bootgrid variables, Event for validation (on-blur). |
AdminHomeJS.php | Frontend, form specific validation and patterns, validation objects, Events for specific validation (on-blur), AJAX getters for the form, AJAX CRUD, Timed Events for the form. |
App.php | Main application PHP page, which contains the JavaScript .ready method. |
App-co.php | Common PHP file which contains the link between the PHP-Frontend and the PHP-Backend, such as the Database connection and the common include files.
For this framework it must contain the AJAX actions and request definitions. |
App-be.php | Backend PHP file which contains the AJAX-Backend implementations.
Specific for the PHP deferred loading this file contains the Form, Form CRUD and Form CRUD Checks Backend implementations. |
- The App in the FileName is the MainApp, meaning the Main PHP Page. The App is split into 3 files App.php, App-co.php and App-be.php.
- The AdminHome in the FileName is the WebPage Tab (deferred Form).
Form Validation
The Application validation is based on Regex with validation objects (validator).
Validation Object
A validator for a string-name has the following JS-object [2]. <syntaxhighlight lang="javascript"> var validHomeName = {
environ : "Home", name : "<?php echo HS_HOME_NAME; ?>", qid : sprintf('[name="%s"]' , "<?php echo HS_HOME_NAME; ?>"), qcb : sprintf('[name="cb%s"]', "<?php echo HS_HOME_NAME; ?>"), pattern : "^[A-Za-z0-9À-ž&@#$\\*\\+\\'\\<\\>\\.\\,\\;\\:\\-\\_\\(\\)\\[\\]\\/ \\n]{3,20}", message : "Length [3..20], alphanumeric", minlen : 3, maxlen : 20, flag : "gi", value : "", status : jsoStatus
}; </syntaxhighlight>
Field element | Field description |
---|---|
environ | The environment for the form. In this case it is all about a house/home. |
name | The name (label of the html-element). The PHP-Label-Definition is made in the AdminHomeDb.php OUTSIDE the class definition. |
qid | The jQuery-Selector name. Enables the getting and settings of values in the form. |
qcb | The jQuery-Selector help/error field name. Enables the validation errors/warnings below the input-field. |
pattern | The RegEx pattern for the value of the input field. The actual validation of the input field. Please note the pattern has an embedded. |
message | The error message to be sent after an invalid input. |
minlen | The minimum input length, give zero (0) if the input is optional. |
maxlen | The maximum input length |
flag | The Regex flag attribute. |
value | The value of the input field, keep it in sync in the on blur events. |
status | The JavaScript jsoStatus object. This object keeps the last validation results. The jsoStatus object is part of the generic JS-Objects, see AppValidateJS.php. |
AdminHomeDb.php
This PHP file is used in the Frontend and in the Backend.
- [1] Line 1-4 Constant declaration [3].
- [2] Line 5-7 Class AdminHomeDb extends the abstract class CRUD [4].
- [3] Line 8-55 Class AdminHomeDb method createFormHouses creates the Client-Form [5].
- [4] Line 56-73 Class AdminHomeDb method updateAdminHome shows an example (not fully shown) of the CRUD implementation.
<syntaxhighlight lang="php" line> // [1] - The Constant definitions for the creation of the form (PHP) and the AJAX Client call (JavaScript) and the Mapping of the form Variables to the Backend (PHP). const HS_HOME_ID = "hsHomeId"; const HS_HOME_NAME = "hsHomeName";
// [2] - The class declaration. class AdminHomeDb extends CRUD {
/** * [3] Creates the admin form for Home Maintenance. * @param array $aReq Request parameters * @return void */ public function createFormHouses( $aReq ) { $this->log->trace( sprintf( logFormatLBW, __method__, "aReq", print_r($aReq, true)));
$r = sprintf('
%s
', _h("admin-houses-header"));
// Form Houses $r.= sprintf('<form id="adminhouses" class="form-horizontal" role="form" method="post" action="Energy.php" onsubmit="return validateEdit();">');
// House / Home id $r.= sprintf('
<label class="control-label col-sm-3" for="%s">%s:</label>
<input class="form-control" id="%s" type="text" name="%s" placeholder="%s" disabled value="%s">
',
HS_HOME_ID, _h('hs-home-id-label'), HS_HOME_ID, HS_HOME_ID, _h('hs-home-id-placeholder'), "Id"); $r.= sprintf('
<button type="button" class="btn btn-default btn-cons" name="%s" value="Update" title="%s" %s>%s</button> <button type="button" class="btn btn-default btn-cons" name="%s" value="Clear" title="%s" %s>%s</button> <button type="button" class="btn btn-default btn-cons" name="%s" value="Retrieve" title="%s" %s>%s</button> <button type="button" class="btn btn-default btn-cons" name="%s" value="Check" title="%s" %s>%s</button> <button type="button" class="btn btn-default btn-cons" name="%s" value="Delete" title="%s" %s>%s</button>
',
HS_BUT_UPDATE , _h('hs-but-update-title' ), "disabled", _h('hs-but-update') , HS_BUT_CLEAR , _h('hs-but-clear-title' ), "disabled", _h('hs-but-clear') , HS_BUT_RETRIEVE, _h('hs-but-retrieve-title'), "disabled", _h('hs-but-retrieve') , HS_BUT_CHECK , _h('hs-but-check-title' ), "disabled", _h('hs-but-check') , HS_BUT_DELETE , _h('hs-but-delete-title' ), "disabled", _h('hs-but-delete') );
$r.= sprintf('</form>');
return $r; } // createFormHouses
/** * [4] Update (and Inserts) the given admin-home. * @ajax ajaxUpdateAdminHome * @javascript doAHUpdate * @param array $aReq Request parameters * @return array $aRet Response parameters. */ public function updateAdminHome($aReq) { $this->log->trace( sprintf( logFormatLBW, __method__, "aReq", print_r($aReq, true) )); if ( intval($aReq[HS_HOME_ID]) > 0 ) { $query = sprintf("
Update %s Set housename= '%s', .... Where id = %u; ",
$this->popo->getTableName('houses'), $aReq[HS_HOME_NAME], ... }
} // class </syntaxhighlight>
String input
The most common input of a string field has the following structure:
- Line 3 Bootstrap form-group div id
- Line 4 Bootstrap column width for the various devices for/and the label name of the input field
- Line 5 Bootstrap column width for the various devices for/and the input field.
- Line 6 HTML-input with class and name.
- Line 7 Information on content (placeholder)
- Line 8 No autocomplete allowed
- Line 9 The function name and parameters of the onblur event, see line 19.
- Line 10 The help (warning/error) line for validation errors.
- Line 11 The Help class
- Line 12 The validator
- Line 13 The help-name belongs to this name.
- Line 14 result and style.
- LINE 19 Name of the field, the placeholder text and the onblur function and the validation object.
<syntaxhighlight lang="php" line>
// Input for House/Home name $r.= sprintf('
<label class="control-label col-sm-3" for="%s">* %s:</label>
<input type="text" class="form-control" name="%s" placeholder="%s" autocomplete="false" onblur="return %s;"> %s.
',
HS_HOME_NAME, _h('hs-home-label'), HS_HOME_NAME, _h('hs-home-placeholder'), "blurOnAdminHomeName(validHomeName)", HS_HOME_NAME, HS_HOME_NAME, _h('hs-home-required'));
</syntaxhighlight>
AppValidateJS.php
The PHP-file AppValidateJS.php contains the JavaScript generic validators.
- Line 1-10 PHP part with comment and versionnumber.
- Line 12-258 JavaScript part
- Line 12-26 JS object enumValidation is an Enumeration for all validators.
- Line 28-38 JS object jsoStatus is the validation status and is embedded is every validator.
- Line 40-56 Funciton strinValidation convert an enumValidation into its string representation.
- Line 58-85 Function validateEdit is the interface for standard validation and calls the validateMatch.
- Line 87-234 Function validateMatch validates using the Regex embedded in the validator.
- Line 236-257 Function findMismatchChars is an helper for making error message indicating the location of the validation error.
<syntaxhighlight lang="PHP" line>
<?php
/**
Javascript-PHP-File AppValidateJS.php
implements Javascript methods with embedded PHP-blocks inside the code.
The files is specially made for screen validations.
...
- /
const AppValidateJSVersionNumber = "5.0.7.1"; const AppValidateJSVersionDate = "17 May 2018"; ?>
<script> /**
* Enumeration of validations. * @type enum */
var enumValidation = {
UNKOWN : 0, OK : 1, EMPTY : 2, PENDING : 4, ERROR : 8, ERROR_LEN : 16, ERROR_CHARS : 32, ERROR_EXISTANCE: 64
};
/**
* JavaScript object instance to be used for the returning of the status, error and description. * @type JavaScript Object. * @since 5.0.6.1 */
var jsoStatus = {
enum : enumValidation.UNKNOWN, statusstring : "0 - Unknown", error : "", description : ""
};
/**
* Converts an enumeration to a readable string format. * @param enumValue Enumerated integer value * @return string */
function stringValidation(enumValue) {
'use strict'; var met = "stringValidation"; switch(enumValue) { case enumValidation.UNKNOWN : return " 0 - Unknown"; case enumValidation.OK : return " 1 - Ok"; case enumValidation.EMPTY : return " 2 - Ok Empty"; case enumValidation.PENDING : return " 4 - Pending"; case enumValidation.ERROR : return " 8 - Error"; default : return ">8 - Error"; }
} // stringValidation
/**
* Validates the current input of the Edit-Fields. * @param object validEdit JS-Object with validation data. * @param boolean noDialog [Optional] flag not to show the dialog. [true]. * @param boolean * @return boolean true|false */
function validateEdit( validEdit, noDialog ) {
'use strict'; var met = 'validateEdit'; var nod = typeof noDialog !== 'undefined' ? noDialog : true; var ret = validateMatch(validEdit, nod);
if (debug) { logts( sprintf( logFormatLBW, met, "Ret-EnumVal", ret + " = " + stringValidation(ret) )); logts( sprintf( logFormatLBW, met, "enum" , prfStr(validEdit.status.enum) )); }
if (ret <= enumValidation.EMPTY) { $(validEdit.qid).removeClass("error"); $(validEdit.qid).attr("title", ""); return true; } else { $(validEdit.qid).addClass("error"); $(validEdit.qid).attr("title", validEdit.status.description); } return false;
} // validateEdit
/** Validates a text field using the regular expression match method. This is the default method for the getMatch PHP input field. Added the findMismatchChars @param validator Javascript object with
- qid jQuery selector - pattern Regex pattern for validation. - minlen Minimum length - maxlen Maximum length - flag Reqex flag - status Object jsoStatus
@param noDialog Indication to not use directly the dialog and setFocus. [default: false] @global validationE Validation enumeration variable (UNKNOWN, ) @return enumValidation instance (PENDING, EMPTY, ERROR_LEN, ERROR_CHARS, OK)
- /
function validateMatch(validator, noDialog) {
'use strict'; var met = "validateMatch"; var nod = typeof noDialog !== 'undefined' ? noDialog : false; var ti = (window.domLanguage === 31 ? "Foute invoer voor '%s'." : "Invalid input for '%s'."); var me = (window.domLanguage === 31 ? "Leeg veld is niet toegestaan voor veld '%s'." : "Empty field not allowed for field '%s'."); var mi = (window.domLanguage === 31 ? "Ongeldige invoer voor veld '%s'.
%s." : "Invalid input for field '%s'.
%s"); var ml = (window.domLanguage === 31) ? "Ongeldige invoer voor veld '%s'.
Veld lengte tussen %u en %u." : "Invalid input for field '%s'.
Field length between %u en %u." var rFlag = validator.flag; var idValue = $(validator.qid).val(); var r = new RegExp(validator.pattern, rFlag); var duration = 300; var modal = false; var result, aResults;
var tmsg = sprintf(ti, validator.name);
if (debug) { logts( sprintf( logFormatLBW, met, "Enum/qid/Name" , validationE + " for qid: " + validator.qid +", " + validator.name + ".") ); logts( sprintf( logFormatLBW, met, "Validator minLen", validator.minlen) ); }
// Checks for pending validation to prevend circular loops. if (validationE === enumValidation.PENDING) { logtrace( sprintf( logFormatLBW, met, "Validation Enum", "PENDING") ); return enumValidation.PENDING; }
if (debug) { logts( sprintf( logFormatLBW, met, validator.qid , idValue) ); logts( sprintf( logFormatLBW, met, "Pattern", validator.pattern)); }
window.validationOk = true; validator.status = { enum: enumValidation.UNKNOWN, statusstring: "0 - Unknown", error: "", description: ""};
// Checks on minimum length, allowed. if (validator.minlen === 0 && idValue === "") { validator.status.enum = enumValidation.EMPTY; validator.status.error = ""; validator.status.description = sprintf( window.domLanguage === 31 ? "Invoer '%s', is leeg, hetgeen is toegestaan." : "Input '%s', is empty, which is allowed!", validator.name); logtrace( sprintf( logFormatLBW, met, "Validate-EMPTY", validator.status.description)); return enumValidation.EMPTY; } // Checks on minimum length, not-allowed. if (validator.minlen > 0 && idValue === "") { validator.status.enum = enumValidation.ERROR_LEN; validator.status.error = '<?php echo _h("valmatch-empty-field-not-allowed"); ?>'; validator.status.description = validator.status.error; logtrace( sprintf( logFormatLBW, met, "Invalid", "Empty string not-allowed.")); if (! nod) { var bmsg = sprintf(me, validator.name); openIconDialog( "notok", bmsg, tmsg, 450, modal); wait4Blur(validator.qid, duration); } window.validationOk = false; return enumValidation.ERROR_LEN; }
if (validator.minlen > 0 && (validator.minlen > idValue.length || validator.maxlen < idValue.length) ) { validator.status.enum = enumValidation.ERROR_LEN; validator.status.error = sprintf('<?php echo _h("valmatch-invalid-length"); ?>', validator.minlen, validator.maxlen); validator.status.description = validator.status.error; if (! nod) { var bmsg = sprintf(ml, validator.name, validator.minlen, validator.maxlen); openIconDialog( "notok", bmsg, tmsg, 450, modal); // wait4Blur(validator.qid, duration); } window.validationOk = false; return enumValidation.ERROR_LEN; }
if (rFlag.indexOf("g") !== -1) { aResults = idValue.match(r); logtrace( sprintf( logFormatLBW, met, "aResults", r )); if (aResults !== null) { result = aResults[0]; } } else { result = idValue.match(r); }
// Undefined result, meaning the regex matched nothing! if (result === undefined) { if (debug) { logts(sprintf( logFormatLBW, met, "Regex Result" , "undefined")); logts(sprintf( logFormatLBW, met, "Regex Undefined", validator.pattern)); console.dir(aResults); } if (! nod) { var bmsg = sprintf(mi, validator.name, "Regex Result undefined"); openIconDialog( "notok", bmsg, tmsg, 450, modal); wait4Blur(validator.qid, duration); } validator.status.enum = enumValidation.ERROR_CHARS; validator.status.error = '<?php echo _h("valmatch-invalid-input"); ?>'; validator.status.description = validator.status.error; return enumValidation.ERROR_EXISTANCE; }
// Defined result but not equal to idValue if (result !== idValue ) { var jsoFirstInvalidChar = findMismatchChars(idValue, r); validator.status.enum = enumValidation.ERROR_CHARS; validator.status.description = sprintf("Input '%s' : %s", validator.name, jsoFirstInvalidChar.description); logtrace( sprintf( logFormatLBW, met, "Description", validator.status.description)); if (! nod) { var bmsg = sprintf(mi, validator.name, jsoFirstInvalidChar.description); openIconDialog( "notok", bmsg, tmsg, 450, modal); $(validator.qid).focus(); // wait4Blur(validator.qid, duration); } window.validationOk = false; return enumValidation.ERROR_CHARS; }
validator.status.enum = enumValidation.OK; validator.status.description = '<?php echo _h("valmatch-no-errors"); ?>'; validationE = enumValidation.OK; return enumValidation.OK;
} // validateMatch
/**
* Finds the mismatched characters from a string given the pattern. * The function is used after you have found errors. * @param String txt The text string to be examined. * @param RegEx regex The regular expression. * @return ret Object status {index, char, description}. */
function findMismatchChars(txt, regex) {
'use strict'; var met = 'findMismatchChars'; var domLanguage = window.domLanguage; var template = '<?php echo _h("valmatch-mismatched-chars"); ?>'; var ret = {index: 0, char: "", description: ""};
regex.test( txt ); if (regex.lastIndex <= txt.length) { ret.index = 1 + regex.lastIndex; ret.char = txt.substr( regex.lastIndex, 1 ); ret.description = sprintf(template, 1 + regex.lastIndex, txt.substr( regex.lastIndex, 1 )); }
return ret;
} // findMismatchChars </script> </syntaxhighlight>
AdminHomeJS.php
The PHP-file AdminHomeJS.php contains the JavaScript Webpage-tabPage specific validators.
Example onBlur Function
The highlight of the onblur method.
- Line 8 Definition of the method for the logts messages.
- Line 9 Flag for No-Dialog (nod), true = do not show a dialog.
- Line 10 The DOM-Html-Name of the input name
- Line 11 The HTML-Tag for the Validation-warnings-errors.
- Line 12 The value of the input field.
- Line 15 Call to the used validation method (validEdit which calls validMatch).
<syntaxhighlight lang="javascript" line> /**
* Handles the blur on the Home Name (Primairy Key). * @param object validator Validator object. * @return void. */
function blurOnAdminHomeName(validator) {
'use strict'; var met = "blurOnAdminHomeName"; var nod = true; var qName = validator.name; var qStat = sprintf("small#%s-help", qName); var frmName = $( sprintf("[name='%s']", qName) ).val(); logts( sprintf( logFormatLBW, met, qName, frmName ) );
if (validateEdit(validator, nod) === false) { logtrace( sprintf( logFormatLBW, met, 'qName - qStat - Description', qName + " - " + qStat + "-" + validator.status.description)); $(qStat).html(validator.status.description); $(qStat).show(); } else { if (frmName == "") { logtrace( sprintf( logFormatLBW, met, "isAHInDB", "<?php echo _h('valid-date-empty'); ?>" )); $(qStat).html("<?php echo _h('valid-date-empty'); ?>"); $(qStat).show(); isAHInDB = false; } else { $(qStat).hide(); validator.value = frmName; checkAdminHomeOnly(validator, true); } } setAdminHomeButtons(validator, false);
} // blurOnAdminHomeName </syntaxhighlight>
App.php
The PHP-file App.php is the Main-PHP-file. This file is called when opening the Webpage. It contains the PHP code that is executed prior/before any JavaScript is performed. The file always includes the file App-co.php which contains the common includes and all global variables and defines.
jQuery .ready
The Main-PHP file has the JQuery function .ready.
- Line 2 jQuery .ready function.
- Line 12 The jQuery selector for the tabs on Bootstrap.
- Line 17 The Function to perform when this tab is clicked.
<syntaxhighlight lang="javascript" line> /** Document.ready */ $(document).ready(
function() { 'use strict'; var met = ".ready"; ... /** * Detecting the change of a Bootstrap-tab. * The target contains the name of the tab * @param event e The event */ $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { var target = $(e.target).attr("href") // activated tab switch(target) { ... case '#tabAdminHomes' : // AjaxLoader has to be made?? doGetFormAdminHomes(); break; ... }
}); // .ready </syntaxhighlight>
Bootstrap Tab
The bootstrap tab-declaration
<syntaxhighlight lang="javascript" line>
<?php if ( CurRole === 'Admin') : ?>
<?php endif; ?> </syntaxhighlight>
Bootstrap Tab Placeholders
The bootstrap tab-placeholders implementation
<?php if (in_array( CurRole, array("Admin"))) : ?> <div class="tab-pane fade" id="tabAdminHomes"> <!-- ++ Tab panes +++++++++++++++++++++++++++++++++++++++++++++ tabAdminHomes ++ --> <div class="container-fluid"> <div id="tabHomeImg" class="row"> <div class="col-sm-8 col-md-8 col-lg-8" > <div id="admin-homes"></div> </div> <div class="col-sm-4 col-md-4 col-lg-4 w-bottom-margin formula-text-right" style="background-color:#A4F7FF;"> <?php printf("<h5>%s</h5>", _h('sidebar-admin-homes') ); printf("<p>%s</p>", _h('sidebar-admin-homes-text')); ?> <br><br> <center> <img class="url" src="../Images/Admin-Maintenance.jpg" title="Admin Homes Maintenance."> <p class="caption"><?php echo _h('sidebar-admin-homes-caption'); ?></p> </center> </div> <div class="col-sm-12 col-md-12 col-lg-12"> <?php BootGrid::tableAdminHomes(); ?> </div> </div> <br> </div> </div> <?php endif; ?>
App-co.php
The PHP-file App-co.php contains the common PHP code, global constant defines and constants.
- ToDo
App-be.php
The PHP-file App-be.php implements the AJAX-Backend calls.
It therefor needs all defines and constants made in the App-co.php.
- ToDo