When loading web-fonts, we often see a brief un-styled moment before the browser applies the actual font. Gladly Typekit and also Google Web Fonts provide an option around that.
Both are using WebFont Loaders to help handle those brief moments.
TypeKit Webfont Loader Example
1 2 3 4 5 6 7 |
(function(d) { var config = { kitId: '***' , // Your Kit ID scriptTimeout: 3000 }, h=d.documentElement,t=setTimeout(function(){h.className=h.className.replace(/\bwf-loading\b/g,"")+" wf-inactive";},config.scriptTimeout),tk=d.createElement("script"),f=false,s=d.getElementsByTagName("script")[0],a;h.className+=" wf-loading";tk.src='//use.typekit.net/'+config.kitId+'.js';tk.async=true;tk.onload=tk.onreadystatechange=function(){a=this.readyState;if(f||a&&a!="complete"&&a!="loaded")return;f=true;clearTimeout(t);try{Typekit.load(config)}catch(e){}};s.parentNode.insertBefore(tk,s) })(document); |
This adds a class name to the <html> element during loading
1 |
<html class="wf-loading"> |
This is removed when loading is done. This allows us to hide content until all fonts are loaded.
Webfonts Loading
1 2 3 |
.wf-loading * { opacity: 0; } |
This also adds classes once the webfonts have been loaded, which allows us to add some transitions to reveal the content again.
Webfonts Loaded
1 2 3 4 5 6 7 |
.wf-active *, .wf-inactive * { -webkit-transition: opacity 1s ease-out; -moz-transition: opacity 1s ease-out; -o-transition: opacity 1s ease-out; transition: opacity 1s ease-out; } |
One last thing. You should add the webfont loader early in your content, so that it can do its magic before anything else is being loaded.
Parent
1 2 3 4 5 |
var ParentView = Backbone.View.extend({ events: { 'click': 'onclick' } }); |
Child
1 2 3 4 5 6 7 8 |
var ChildView = ParentView.extend({ events: function(){ return _.extend({}, ParentView.prototype.events,{ 'click':'childClick', }); } }); |
Adding Metaboxes in WordPress to your admin areas normally involves 3 steps:
This can be done much easier using Custom Metaboxes and Fields for WordPress, which is a metabox, custom fields and forms library. The library provides a bunch of custom fields and also allows you to add your own easily. Much easier to build new Metaboxes fast!
List of field types included:
Simple example from the CMB2 Github page
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
if ( file_exists( __DIR__ . '/cmb2/init.php' ) ) { require_once __DIR__ . '/cmb2/init.php'; } elseif ( file_exists( __DIR__ . '/CMB2/init.php' ) ) { require_once __DIR__ . '/CMB2/init.php'; } add_action( 'cmb2_init', 'cmb2_sample_metaboxes' ); /** * Define the metabox and field configurations. */ function cmb2_sample_metaboxes() { // Start with an underscore to hide fields from custom fields list $prefix = '_yourprefix_'; /** * Initiate the metabox */ $cmb = new_cmb2_box( array( 'id' => 'test_metabox', 'title' => __( 'Test Metabox', 'cmb2' ), 'object_types' => array( 'page', ), // Post type 'context' => 'normal', 'priority' => 'high', 'show_names' => true, // Show field names on the left // 'cmb_styles' => false, // false to disable the CMB stylesheet // 'closed' => true, // Keep the metabox closed by default ) ); // Regular text field $cmb->add_field( array( 'name' => __( 'Test Text', 'cmb2' ), 'desc' => __( 'field description (optional)', 'cmb2' ), 'id' => $prefix . 'text', 'type' => 'text', 'show_on_cb' => 'cmb2_hide_if_no_cats', // function should return a bool value // 'sanitization_cb' => 'my_custom_sanitization', // custom sanitization callback parameter // 'escape_cb' => 'my_custom_escaping', // custom escaping callback parameter // 'on_front' => false, // Optionally designate a field to wp-admin only // 'repeatable' => true, ) ); } |
The current Google Analytics Dashboard for WordPress integrates no Opt-Out to disable tracking, which is required in Europe. Here is a workaround for that.
Paste this into your theme function.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
if ( !is_user_logged_in() && class_exists('GADWP_Manager') ) { add_action('wp_head', 'ga_optout_script', 1); } function ga_optout_script(){ // Read GA Dashboard Settings $options = (array) json_decode(get_option('gadash_options')); // Get GA Dashboard profiles foreach($options['ga_dash_profile_list'] as $profile){ $ga_ua_profile[$profile[1]] = $profile[2]; } // Get active Analytics profile for the current website $active = $options['ga_dash_tableid_jail']; $activeUA = $ga_ua_profile[$active]; // Add opt-out script + active UA echo "<script> var gaProperty = '".$activeUA."'; var disableStr = 'ga-disable-' + gaProperty; if (document.cookie.indexOf(disableStr + '=true') > -1) { window[disableStr] = true; } function gaOptout() { document.cookie = disableStr + '=true; expires=Thu, 31 Dec 2099 23:59:59 UTC; path=/'; window[disableStr] = true; } </script>"; } |
Then add this to your data privacy document:
1 |
<a href="javascript:gaOptout()">Disable Google Analytics tracking</a> |
When the defer attribute is present in the script tag, it specifies that the script is executed when the page has finished parsing. A requirement that is enforced by Google for example (Page-Speed ranking).
Currently the WordPress wp_enqueue_script provides no easy way to add new attributes, but there is a way around that :)
The below hack / filter needs to be added to your theme function.php. The filter should be your preferred solution, as the clean_url filter has been deprecated.
Make sure that your website / theme is still loading after adding these changes. If needed you can add more exceptions, as I did for jQuery.
Hack for WordPress before 4.1
1 2 3 4 5 6 7 8 |
if(is_admin() === FALSE){ function defer_parsing_of_js ( $url ) { if ( FALSE === strpos( $url, '.js' ) ) return $url; if ( strpos( $url, 'jquery.js' ) ) return $url; return "$url' defer "; } add_filter( 'clean_url', 'defer_parsing_of_js', 11, 1 ); } |
Filter for WordPress 4.1+
Updated: 26.04.2015 – using clean Regexp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
if(is_admin() === FALSE){ function add_js_defer($tag, $handle, $src){ if( strpos( $tag, 'jquery.js' ) ) return $tag; preg_match_all('/([\w\-.:]+)\s*=\s*("[^"]*"|\'[^\']*\'|[\w\-.:]+)/i', $tag, $match); $newtag = "<script "; foreach($match[1] as $key=> $val){ $newtag .= $val."=".$match[2][$key]." "; } $newtag .= "defer></script>"; return $newtag.PHP_EOL; } add_filter( 'script_loader_tag', 'add_js_defer', 11, 1 ); } |
Compressing your content saves bandwidth and improves render time, particular on devices with slow internet connections. Compression allows your web server to provide smaller file sizes that load faster for your visitors. Compression of your HTML and CSS files with gzip typically saves around 50 to 70 % of the file size.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# compress text, html, javascript, css, xml: AddOutputFilterByType DEFLATE text/plain AddOutputFilterByType DEFLATE text/html AddOutputFilterByType DEFLATE text/xml AddOutputFilterByType DEFLATE text/css AddOutputFilterByType DEFLATE application/xml AddOutputFilterByType DEFLATE application/xhtml+xml AddOutputFilterByType DEFLATE application/rss+xml AddOutputFilterByType DEFLATE application/javascript AddOutputFilterByType DEFLATE application/x-javascript # Or, compress certain file types by extension: <files *.html> SetOutputFilter DEFLATE </files> |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
gzip on; gzip_comp_level 2; gzip_http_version 1.0; gzip_proxied any; gzip_min_length 1100; gzip_buffers 16 8k; gzip_types text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript; # Disable for IE < 6 because there are some known problems gzip_disable "MSIE [1-6].(?!.*SV1)"; # Add a vary header for downstream proxies to avoid sending cached gzipped files to IE6 gzip_vary on; |
A good candidate is the WP Far Future Expiration Plugin ,which not only activates GZIP compression but adds file expiration for various static file types.
Link
Check speed improvement before and after
Will be adding some basic information about using the current Google API with the PHP SDK, this includes Google+, Youtube and other services. For setting up the actual API access see the Google documentation and Google Developer Console
1 2 3 4 5 6 7 8 9 10 11 12 |
require _DIR_TO_AUTOLOAD_SDK_.'/google_sdk/autoload.php'; // Required Information // OAuth 2.0 $google_client_id = _GOOGLE_CLIENT_ID_; $google_client_secret = _GOOGLE_CLIENT_SECRET_; // Public API-Access $google_redirect_url = 'http://yourdomain.com/google_login/?option=do_login'; // API-key for a server application! $google_developer_key = _GOOGLE_DEVELOPER_KEY_; |
1 2 3 4 5 6 7 |
$gClient = new Google_Client(); $gClient->setAccessType('offline'); $gClient->setApplicationName('Application Name'); $gClient->setClientId($google_client_id); $gClient->setClientSecret($google_client_secret); $gClient->setRedirectUri($google_redirect_url); $gClient->setDeveloperKey($google_developer_key); |
1 2 3 4 5 6 7 8 9 10 |
$gClient->setScopes(array( 'https://www.googleapis.com/auth/plus.me', 'https://www.googleapis.com/auth/plus.login', 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', 'https://gdata.youtube.com', 'https://www.googleapis.com/auth/youtube.readonly', 'https://www.googleapis.com/auth/youtubepartner' ) ); |
1 |
$plus = new Google_Service_Plus($gClient); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
session_start(); // A simple check for the login to get a fresh token if($_GET['option'] == "do_login" && empty($_SESSION['google_token'])){ // Check for redirect from Google & Auth if (empty($_GET['code'])) { // Create Auth redirect header('Location: '.$gClient->createAuthUrl()); } else { $gClient->authenticate($_GET['code']); $access_token = $gClient->getAccessToken(); //Store the token in a session or your database for later use $_SESSION['google_token'] = $access_token; } } // You have already logged in if(!empty($_SESSION['google_token'])){ $gClient->setAccessToken($_SESSION['google_token']); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Making sure we have a valid access token if($gClient->getAccessToken()) { try{ // Get your activities $optParams = array('maxResults' => 100); $activities = $plus->activities->listActivities('me', 'public', $optParams); print_r($activities); } } catch (Google_ServiceException $e) { $htmlBody .= sprintf('<p>A service error occurred: <code>%s</code></p>', htmlspecialchars($e->getMessage())); } catch (Google_Exception $e) { $htmlBody .= sprintf('<p>An client error occurred: <code>%s</code></p>', htmlspecialchars($e->getMessage())); } |
Will be adding some basic information about using the current Facebook API with the PHP SDK. Have been updating a lot of old API integrations over the past 2 months and finally solved many badly documented areas of the new API. Will be updating this in the future, as I dive deeper into the latest API changes.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
require_once _DIR_TO_AUTOLOAD.'/facebook_sdk/autoload.php'; use Facebook\FacebookSession; use Facebook\FacebookRedirectLoginHelper; use Facebook\FacebookRequest; use Facebook\FacebookResponse; use Facebook\FacebookSDKException; use Facebook\FacebookRequestException; use Facebook\FacebookAuthorizationException; use Facebook\GraphObject; use Facebook\Entities\AccessToken; use Facebook\HttpClients\FacebookCurlHttpClient; use Facebook\HttpClients\FacebookHttpable; |
Depending on what you want to do with the API, the above needs to be adjusted. Check the Facebook API documentation for more details.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
// Session should be started after all files have been included session_start(); // init app with app id and secret FacebookSession::setDefaultApplication( _FB_APP_ID_, _FB_APP_SECRET_ ); // login helper with redirect_uri $helper = new FacebookRedirectLoginHelper( 'http://yourdomain.com/FB_LOGIN/'); // Check for Facebook redirect & retrieve session try { $session = $helper->getSessionFromRedirect(); } catch( FacebookRequestException $ex ) { // When Facebook returns an error } catch( Exception $ex ) { // When validation fails or other local issues } if ( isset( $session ) ) { /* Store the session for later reuse, in this case even getting a long lived session. You should store this in your database. The session can be reused with: $session = new FacebookSession($_SESSION['long']->getToken()); */ if(empty($_SESSION['long'] )){ $_SESSION['long'] = $session->getLongLivedSession(); } // graph api request for user data try { $user_profile = (new FacebookRequest( $session, 'GET', '/me' ))->execute()->getGraphObject(GraphUser::className()); echo "Name: " . $user_profile->getName(); } catch(FacebookRequestException $e) { echo "Exception occured, code: " . $e->getCode(); echo " with message: " . $e->getMessage(); } } else { // show login url if no session available // request special permissions, optional. $params = array( 'manage_pages', 'publish_pages','publish_actions','user_videos' ); echo '<a href="' . $helper->getLoginUrl($params ) . '">Login</a>'; } |
I had a problem with the tag pagination throwing 404 errors and was forced to integrate a custom query into the 404 page, while fixing the rewrite rules ;)
This is how you force a different HTTP response header with WordPress, in this case a simple HTTP/1.1 200 OK.
1 2 |
$GLOBALS['wp_query']->is_404 = false; status_header( 200 ); |
Some tweaking to add your own classes to the body tag
1 2 3 4 5 6 7 |
add_filter( 'body_class', 'my_class_names' ); function my_class_names( $classes ) { $classes[] = "archive tag " ; return $classes; } |
“countUp.js is a dependency-free, lightweight JavaScript “class” that can be used to quickly create animations that display numerical data in a more interesting way.”