Site Profile: Longkai Group


The Longkai Group is a large commercial real estate development firm from China, who had recently rebranded and wanted to create a new online presence. Elevated Third proposed a site based around the impressive imagery of their projects and the surrounding areas. The concept was to make the site look its best on larger screens, with each page filling the browser, and transitions between pages instead of fresh page loads.


The site was planned to be a set of fullscreen pages organized by the menu structure. Each "main" page, or the sections listed in the main navigation, would be stacked vertically, so that a visitor could simply scroll down through these pages.

The "sub" pages would be arranged horizontally, so from About clicking the next button will lead to about/history.

This concept was more challenging because each "page" of the site needed to fill the browser, preventing the surrounding pages from being visible until a visitor clicked or scrolled to another page on purpose.

Subpages have a unique layout, with a large banner image across the top and two columns of content below. The Project pages are identical, except the banner is actually a full screen slideshow which can be exposed by collapsing the content area.


A major goal of this project was to leverage the strong content management features of Drupal while creating a "single page" site that is outside the traditional scope of Drupal.

The "grid" layout of the pages translated well to a nested array:

  [Projects][Project Detail]...

Nested arrays match how the menu structure in Drupal works, as well.

This formed the basis for managing the site layout in Drupal— using the menu system to control page "placement".

First, the entire site was built and the majority of theming was completed. Each page loads normally, and every menu item points to the aliased path.

Every page has a Chinese translation, so the site is entirely duplicated in Chinese. With all the pages created and added to menus, it became possible to use those menus to build the model for the "grid" layout.

Since the site was multilingual, it was necessary to build two identical arrays, one for each language. Each array contained the pages, organized in this hierarchy, along with data necessary to display the pages.

[prism:php] $tree = menu_tree_all_data('main-menu'); $menu[$langcode] = _longkai_utils_load_menu($tree, $langcode); [/prism:php]

The Drupal function


returns all the menu items, with English and Chinese mixed together. It was necessary to process that tree to get information about each node and separate the languages into their own arrays.


[prism:php] function _longkai_utils_load_menu($tree, $langcode) { $menu = array(); // Give each item a coordinate. $row = 0; $col = 0; foreach ($tree as $key => $item) { if ($item['link']['language'] == $langcode) { $pos = array_search($key, array_keys($tree)); $menu[$pos][] = _longkai_utils_parse_menu($item, $row, $col); // Not going truly recursive because we only want 1 level deep. if (!empty($item['below'])) { foreach ($item['below'] as $child) { $col++; $menu[$pos][] = _longkai_utils_parse_menu($child, $row, $col); } } // Reset for next row. $row++; $col = 0; } } // Reset keys (0,1,2,3...) instead of original menu key (0,2,4,6...) $menu = array_values($menu); return $menu; } [/prism:php]

This function returns an array of "menu items". Each item is loaded into the array and, if it has child items, each child is looped over and added in as well. The array is "nested" as described and a value for row and col is incremented and stored with the menu item itself. For example, Home would be row 0, col 0, while About will be row 1, col 0. About/History will be row 1, col 1 and so on. The array itself is built with an array as each row, and each array item being a page.

  [0][0 (home)]
  [1][0 (about)][1 (history)][2 (team)]...
  [2][0 (projects)][1 (project detail)]...
  [3][0 (careers)]
  [4][0 (contact)]

As each menu item is iterated over, the


function builds an array for that item with all the information needed to build it out on the page, including its node ID,  aliased path, and coordinates in the grid.


[prism:php] function _longkai_utils_parse_menu($item, $row, $col) { $path = $item['link']['link_path']; $title = $item['link']['title']; $alias = _longkai_utils_prefix_alias($path, $item); $nid = array_pop(explode("/", $path)); $page_title = $title . ' | ' . variable_get('site_name'); $menu_item = array( 'row' => $row, 'col' => $col, 'nid' => $nid, 'title' => $title, 'pTitle' => $page_title, 'path' => $path, 'alias' => $alias, ); return $menu_item; } [/prism:php]

This array is built and cached on the first page load, stored in a namespaced variable and passed into Drupal.settings.

[prism:php] drupal_add_js($menu, 'setting'); [/prism:php]

The entire structure of the site is now a javascript object, which is used to control the dynamic parts of the site.

Having all the information about each "page" in its own object is crucial to being able to load each page in its correct position and keeping track of which page is active.

We used Underscore.js heavily for searching through arrays, one of the most frequently used is the _.findWhere method.

[prism:javascript] activeItem = _.findWhere(this.menuList, {row: row, col:col}); [/prism:javascript]

The only catch is that it only works on flat arrays, not the nested one described above. Underscore came through there, too— with the _.flatten method. Flatten turned the "structured" 2D array into a searchable list.

Building the Grid

The grid structure translated directly into html. On each page load, the "active" page and its coordinates in the grid were determined. This page becomes the center of the grid, and a "column"


is wrapped around it. If it has any siblings, those columns are built to either side, then the columns are wrapped in a "row"


. The array is looped over, building the next row down, and a


for each column. Then it counts up, building any rows and columns above.


Each "neighbor" page is then loaded via ajax. For example, if page (1, 2) was the first pageview, the grid of empty


's would be built around it, then the pages (1, 1), (1, 3), (0, 0) and (2, 0) would be loaded. The rest remain empty until either a page at that coordinate or one of its adjacent neighbors is loaded.


The neighbors are "preloaded" so that moving to the next page appears as seamless as possible.


Each page loads when a user clicks a navigation item, scrolls up or down with the mouse, or clicks one of the dynamic "Next" buttons that appear in sections with subpages. You can also move through the site using the WASD keys.

The page "movement" means that only the first page loads, the rest are preloaded with AJAX. That means to get URLs and a browser history we have to take advantage of the HTML5 History API. For the best cross-browser support we used History.js which provides its own methods for pushing a new State.

Each page "move" triggers the


method which can be passed info about the current page. Since the entire page "object" was available it is stored there, along with the page title and the page alias. The title and alias are used directly, updating the URL and page title in the browser. Hitting the "back" button can now trigger page movement in reverse. Since the necessary information is stored in the browser State, a visitor can "rewind" through the site.


Although History.js supports older browsers, this seemed like a good opportunity to provide a fallback. Using Modernizr, it was simple to test for


, and only build the grid feature for browsers that pass. Any other browser gets a single page load with no preloading or grid movement. That decision was based both on knowing how important the back button and browser history have always been to navigating websites, and also when testing the site on older computers that struggled to display and scroll through pages with large images.


The guiding idea in developing the Longkai Group site was Progressive Enhancement, where the site gives the user the best possible experience their browser is capable of. That isn't just a buzzword, it actually describes the development process. Each phase of the site was fully functional, letting older browsers fallback to a completely usable site but providing a more dynamic and fluid experience for modern browsers.

Check out the live site at