This article explains how to create custom promoted links/tiles in SharePoint with custom sorting.

Technologies Used

  • HTML CSS for building tile-like structure.
  • CSOM to get data from SharePoint list.
  • Knockout JS for binding list data with UI.

Prerequisite

  1. Add the promoted links app to your site.(I have given it a name as “CustomTiles”). You can use a custom list as well, with the same columns as in promoted links.
  2. Add a picture library for background images of the promoted links. (Given name as “picsForTiles”).
  3. Add some data in promoted links app, i.e., “CustomTiles” list.

Below is the screenshot of the application with some random data.

SharePoint
SharePoint

HTML

Add the following HTML with required files in HTML file.

  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">
  2. <html>
  3. <head>
  4. <title>Custom Promoted Links</title>
  5. <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  6. <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
  7. <script src="/SiteAssets/JS/getTilesData.js"></script>
  8. <link rel="stylesheet" href=" /SiteAssets/CSS/customTiles.css" /> </head>
  9. <body>
  10. <!--HTML Content--><input type="button" value="Sort By Title" data-bind="click: sortedtilesByTitle()"> <input type="button" value="Sort By Order" data-bind="click: sortedtilesByOrder()"><br/><br/>
  11. <div class="mainContainer" data-bind="foreach:tiles">
  12. <div class='card slide-up'>
  13. <div><img class='image' data-bind="attr:{src: backImage}"> </div>
  14. <div class='caption'>
  15. <h2 class='caption-title' data-bind="text:title"></h2>
  16. <p class='caption-content' data-bind="text:desc"> </p>
  17. </div>
  18. </div>
  19. </div>
  20. </body>
  21. </html>

CSS

Content of customTiles.css file is as below.

  1. .card {
  2. position: relative;
  3. width: 200 px;
  4. height: 200 px;
  5. float: left;
  6. margin: 0 10 px 10 px 0;
  7. cursor: pointer;
  8. background - color: #ffffff;
  9. overflow: hidden;
  10. box - shadow: 0 3 px 6 px rgba(0, 0, 0, 0.16),
  11. 0 3 px 6 px rgba(0, 0, 0, 0.23);
  12. }.image {
  13. width: 200 px;
  14. height: 200 px;
  15. background - position: center;
  16. background - size: cover;
  17. background - repeat: no - repeat; - webkit - transition - property: -webkit - transform;
  18. transition - property: -webkit - transform;
  19. transition - property: transform;
  20. transition - property: transform,
  21. -webkit - transform; - webkit - transition - duration: 0.4 s;
  22. transition - duration: 0.4 s;
  23. }.caption {
  24. position: absolute;
  25. top: auto;
  26. bottom: 0;
  27. opacity: 0;
  28. left: 0;
  29. width: 100 % ;
  30. height: 100 px;
  31. background - color: #ffffff;
  32. padding: 15 px; - webkit - transform: translateY(100 % );
  33. transform: translateY(100 % ); - webkit - backface - visibility: hidden;
  34. backface - visibility: hidden; - webkit - transition: opacity 0.1 s 0.3 s,
  35. -webkit - transform 0.4 s;
  36. transition: opacity 0.1 s 0.3 s,
  37. -webkit - transform 0.4 s;
  38. transition: transform 0.4 s,
  39. opacity 0.1 s 0.3 s;
  40. transition: transform 0.4 s,
  41. opacity 0.1 s 0.3 s,
  42. -webkit - transform 0.4 s;
  43. }.caption - title {
  44. margin - top: 0;
  45. margin - bottom: 10 px;
  46. }.caption - content {
  47. margin: 0;
  48. }.caption - link {
  49. color: #724b31;
  50. text - decoration: none;
  51. opacity: 1; - webkit - transition - property: opacity;
  52. transition - property: opacity; - webkit - transition - duration: 0.15 s;
  53. transition - duration: 0.15 s; - webkit - transition - timing - function: cubic - bezier(0.39, 0.58, 0.57, 1);
  54. transition - timing - function: cubic - bezier(0.39, 0.58, 0.57, 1);
  55. }.caption - link: hover {
  56. opacity: 0.65;
  57. }.slide - up: hover.image {
  58. -webkit - transform: translateY(-50 px);
  59. transform: translateY(-50 px);
  60. }.slide - up: hover.caption {
  61. opacity: 1; - webkit - transform: translateY(0 px);
  62. transform: translateY(0 px); - webkit - transition: opacity 0.1 s,
  63. -webkit - transform 0.4 s;
  64. transition: opacity 0.1 s,
  65. -webkit - transform 0.4 s;
  66. transition: transform 0.4 s,
  67. opacity 0.1 s;
  68. transition: transform 0.4 s,
  69. opacity 0.1 s,
  70. -webkit - transform 0.4 s;
  71. }

JavaScript (gettilesData.js)

I have used knockout.js for binding the logic. This is a self-explanatory JavaScript file.

  1. //declare variables
  2. var completeListItems = null;
  3. var listName = 'CustomTiles';
  4. $(document).ready(function() {
  5. //fetch items from list on page load
  6. SP.SOD.executeFunc('sp.js', 'SP.ClientContext', function() {
  7. mainFunction();
  8. });
  9. });
  10. //main function to activate knockout
  11. function mainFunction() {
  12. completeListItems = new tilesListViewModel();
  13. retrivelistItems();
  14. ko.applyBindings(completeListItems);
  15. }
  16. function tilesList(title, desc, tileOrder, backImage, linkLocation) {
  17. var self = this;
  18. self.title = title;
  19. self.desc = desc;
  20. self.tileOrder = tileOrder;
  21. self.backImage = backImage;
  22. self.linkLocation = linkLocation;
  23. }
  24. //view model
  25. function tilesListViewModel() {
  26. var self = this;
  27. self.tiles = ko.observableArray([]); //observable array
  28. //to add tiles data
  29. self.AddTiles = function(title, desc, tileOrder, backImage, linkLocation) {
  30. self.tiles.push(new tilesList(title, desc, tileOrder, backImage, linkLocation));
  31. }
  32. //sort tiles by order
  33. self.sortedtilesByOrder = function() {
  34. self.tiles.sort(function(left, right) {
  35. return left.tileOrder == right.tileOrder ? 0 : (left.tileOrder < right.tileOrder ? -1 : 1)
  36. });
  37. }
  38. //sort tiles by title
  39. self.sortedtilesByTitle = function() {
  40. self.tiles.sort(function(left, right) {
  41. return left.title == right.title ? 0 : (left.title < right.title ? -1 : 1)
  42. });
  43. }
  44. } //End view model
  45. //function to get list items from sharepoint using CSOM
  46. function retrivelistItems() {
  47. var clientContext = new SP.ClientContext.get_current();
  48. var oList = clientContext.get_web().get_lists().getByTitle(listName);
  49. var camlQuery = new SP.CamlQuery();
  50. camlQuery.set_viewXml("<View><RowLimit>20</RowLimit></View>");
  51. this.collListItem = oList.getItems(camlQuery);
  52. clientContext.load(collListItem);
  53. clientContext.executeQueryAsync(Function.createDelegate(this, this.querySuccess), Function.createDelegate(this, this.queryFail));
  54. }
  55. //success callback
  56. function querySuccess(sender, args) {
  57. var listItemEnumerator = collListItem.getEnumerator();
  58. while (listItemEnumerator.moveNext()) {
  59. var oListItem = listItemEnumerator.get_current();
  60. var tileTitle = oListItem.get_item('Title');
  61. var tileDesc = oListItem.get_item('Description');
  62. var tileOrder = oListItem.get_item('TileOrder');
  63. var tileBackImage = oListItem.get_item('BackgroundImageLocation').get_url();
  64. var linkLoc = oListItem.get_item('LinkLocation').get_url();
  65. //fill the data from list
  66. completeListItems.AddTiles(tileTitle, tileDesc, tileOrder, tileBackImage, linkLoc);
  67. }
  68. }
  69. //failure function callback
  70. function queryFail(sender, args) {
  71. alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
  72. }

Add a Content Editor to SharePoint page and refer to the HTML file. The end result will be something like below.

SharePoint

On mouse hover, the tile displays Title and Description. You can add more data from the list if required.

SharePoint

Clicking on the "sort by title" or "sort by order" option will sort the tiles accord the ng to title (A-Z) or order provided in the list (1-onwards sequentially).

You can customize the CSS for more transition effects.