{"_id":"56b5467085a6922300d1c54b","version":{"_id":"5668fab608f90021008e8832","__v":19,"project":"5668fab608f90021008e882f","createdAt":"2015-12-10T04:08:22.769Z","releaseDate":"2015-12-10T04:08:22.769Z","categories":["5668fab708f90021008e8833","569740f124490c3700170a64","569742b58560a60d00e2c25d","569742bd0b09a41900b2446c","569742cd69393517000c82b3","569742f459a6692d003fad8f","569743020b09a41900b2446d","5697430b69393517000c82b5","56a17776470ae00d00c30642","56a2c48a831e2a0d0069b1ad","56b535757bccae0d00e9a1cd","56e1ff6aa49fdc0e005746b5","57e1c88115bf6522002a5e4e","57fa65275ba65a17008b988f","57fbeea34002550e004c032e","58474584889b6c2d00fb86e9","58475dcc64157f0f002f1907","587e7b5158666c2700965d4e","58a349fc30852819007ba083"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"1.18.0","version":"1.18"},"user":"56839cf74aecbd0d00a4659e","__v":7,"parentDoc":null,"category":{"_id":"569742bd0b09a41900b2446c","__v":9,"pages":["5697455824490c3700170a69","569747128400d52d00dd56a4","5697476b8560a60d00e2c262","569747a4c8ded91700307b92","5697489c59a6692d003fad96","56a1500244f3d80d00a2c388","56a2c51906150b0d002ad264","56b548b75f1cf00d00cc4773","56b54ae37719bb1900143093"],"project":"5668fab608f90021008e882f","version":"5668fab608f90021008e8832","sync":{"url":"","isSync":false},"reference":false,"createdAt":"2016-01-14T06:39:57.496Z","from_sync":false,"order":6,"slug":"tuning-liftigniter-javascript","title":"Rendering,Tracking and A/B Testing Widgets (JavaScript)"},"project":"5668fab608f90021008e882f","updates":[],"next":{"pages":[],"description":""},"createdAt":"2016-02-06T01:03:44.023Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"settings":"","auth":"required","params":[],"url":""},"isReference":false,"order":7,"body":"[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"1. Multiple widgets on the same pageview: illustration\"\n}\n[/block]\nYou need to register all the widgets that you wish to render. Let’s say your two widget names are \"bottom-widget\" and \"trending-widget\". You need to follow the same steps as for a single widget, except that you need to do it for each widget.\n\n***Step 1*** Add recommendation unit placeholders for each widget.\n\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<div id=\\\"li-bottom-recommendation-unit\\\"></div>\\n<div id=\\\"li-trending-recommendation-unit\\\"></div>\",\n      \"language\": \"html\"\n    }\n  ]\n}\n[/block]\n***Step 2*** Add mustache templates for each of your widgets\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<script type=\\\"application/mustache\\\"\\n        id=\\\"li-bottom-recommendation-template\\\">\\n  {{#items}}\\n  <div class='recommended_item'>\\n    <a href=\\\"{{url}}\\\">\\n      <img src=\\\"{{thumbnail}}\\\" width=\\\"150\\\" height=\\\"150\\\" />\\n      <span class=\\\"title\\\">{{title}}</span>\\n    </a>\\n  </div>\\n {{/items}}\\n</script>\\n\\n<script type=\\\"application/mustache\\\"\\n        id=\\\"li-trending-recommendation-template\\\">\\n  {{#items}}\\n  <div class='recommended_item'>\\n    <a href=\\\"{{url}}\\\">\\n      <img src=\\\"{{thumbnail}}\\\" width=\\\"150\\\" height=\\\"150\\\" />\\n      <span class=\\\"title\\\">{{title}}</span>\\n    </a> by {{author}}\\n  </div>\\n {{/items}}\\n</script>\",\n      \"language\": \"html\"\n    }\n  ]\n}\n[/block]\n***Step 3***\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Register calls for different widgets.\\n$p('register', {\\n\\tmax: 6, // Number of items you want to show\\n\\twidget: 'bottom-widget',\\n\\topts : {},\\n\\tcallback: function(resp) {\\n    // Query selector should match div name from Step 1\\n\\t\\tvar el = document.querySelector('#li-bottom-recommendation-unit');\\n    // Template should match mustache template name from Step 2\\n\\t\\tvar template = document.querySelector('#li-bottom-recommendation-template').innerHTML;\\n    // Basically Mustache.render(template, resp);\\n\\t\\tel.innerHTML = $p('render', template, resp);\\n    // Remove in production\\n    console.log(\\\"Debug was set to true; disable it in production!\\\"); \\n\\t\\t$p('track', {\\n\\t\\t\\telements: document.querySelectorAll('#li-bottom-recommendation-unit > div.recommended_item'), \\n      // Match widget name\\n      name: 'bottom-widget',\\n      // Source \\\"LI\\\" indicates recommendations are provided\\n\\t\\t\\t// by LiftIgniter\\n\\t\\t\\tsource: 'LI',\\n      // Optional parameters to track with each event.\\n      // If the corresponding 'register' call had optional\\n      // fields, exactly those fields MUST be present here.\\n\\t\\t\\topts: {},\\n\\t\\t\\t_debug : true\\n    });\\n  }\\n});\\n\\n$p('register', {\\n\\tmax: 4, // Number of items you want to show\\n  widget: 'trending-widget',\\n\\topts : {},\\n  callback: function(resp) {\\n    // Query selector should match div name from Step 1\\n\\t\\tvar el = document.querySelector('#li-trending-recommendation-unit');\\n\\t\\t// Template should match mustache template name from Step 2\\n    var template = document.querySelector('#li-trending-recommendation-template').innerHTML;\\n    // Basically Mustache.render(template, resp);\\n\\t\\tel.innerHTML = $p('render', template, resp);\\n    console.log(\\\"Debug was set to true; disable it in production!\\\"); // Remove in production\\n\\t\\t$p('track', {\\n      // Div name from Step 1 and recommendation item div name from Step 2\\n\\t\\t\\telements: document.querySelectorAll('#li-trending-recommendation-unit > div.recommended_item'), \\n      // Match widget name\\n      name: 'trending-widget',\\n      // Source \\\"LI\\\" indicates recommendations are provided\\n\\t\\t\\t// by LiftIgniter\\n\\t\\t\\tsource: 'LI',\\n      // Optional parameters to track with each event.\\n      // If the corresponding 'register' call had optional\\n      // fields, exactly those fields MUST be present here.\\n\\t\\t\\topts: {},\\n\\t\\t\\t_debug : true\\n\\t\\t});\\n\\t}\\n});\\n\\n// Fetch recommendations and trigger callbacks for all registered.\\n$p('fetch');\",\n      \"language\": \"javascript\"\n    }\n  ]\n}\n[/block]\n\n[block:callout]\n{\n  \"type\": \"info\",\n  \"title\": \"Fetching\",\n  \"body\": \"The fetch function must be called exactly once after all calls have been registered. This way we can make sure there are no duplicate recommendations across widgets, and also reduce latency by combining http calls to the model server when possible.\"\n}\n[/block]\n\n[block:callout]\n{\n  \"type\": \"info\",\n  \"body\": \"A widget can be registered multiple times before the fetch. Each time a widget is registered, the previous registration is overwritten. You will see a message in the Javascript console when you re-register a widget.\",\n  \"title\": \"Multiple widget registration\"\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"2. Priority between widgets, deduplication, and number of recommendations per widget\"\n}\n[/block]\nIf you are showing multiple widgets on the same page, the priority between the widgets is determined by the order in which you register the widgets. If a particular widget is more important to you (i.e., you want to show better recommendations for it) be sure to register it first.\n\nIf you have multiple widgets, it is particularly important that you request only as many recommendations per widget as you need for that widget. Otherwise, the deduplication might lead us to discard quality recommendations.\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"3. Request Extra Fields\"\n}\n[/block]\nWe allow only one set of fields across all widgets. Thus, if you need different fields across different widgets, you should fetch all fields that are needed for any of the widgets you are registering.\n\nNote that we do not return fields for inventory items if we do not have those field values for those inventory items.\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"4. Trigger different widgets in different contexts\"\n}\n[/block]\nYou might have different widget names for different user contexts. A typical use case is that your site has different layouts for desktop, mobile, and tablet, and you have different sets of active widgets for these contexts. For instance, you may have both a bottom widget and a trending widget in desktop, but only a bottom widget in mobile.\n\nPlease make sure to only register the widgets that are active in the context, so that the recommendations you fetch are the very best (if you register additional widgets that aren’t being used, you might end up getting poorer-quality recommendations on the widgets you do show because of deduplication).","excerpt":"","slug":"multiple-widgets","type":"basic","title":"Multiple Widgets"}
[block:api-header] { "type": "basic", "title": "1. Multiple widgets on the same pageview: illustration" } [/block] You need to register all the widgets that you wish to render. Let’s say your two widget names are "bottom-widget" and "trending-widget". You need to follow the same steps as for a single widget, except that you need to do it for each widget. ***Step 1*** Add recommendation unit placeholders for each widget. [block:code] { "codes": [ { "code": "<div id=\"li-bottom-recommendation-unit\"></div>\n<div id=\"li-trending-recommendation-unit\"></div>", "language": "html" } ] } [/block] ***Step 2*** Add mustache templates for each of your widgets [block:code] { "codes": [ { "code": "<script type=\"application/mustache\"\n id=\"li-bottom-recommendation-template\">\n {{#items}}\n <div class='recommended_item'>\n <a href=\"{{url}}\">\n <img src=\"{{thumbnail}}\" width=\"150\" height=\"150\" />\n <span class=\"title\">{{title}}</span>\n </a>\n </div>\n {{/items}}\n</script>\n\n<script type=\"application/mustache\"\n id=\"li-trending-recommendation-template\">\n {{#items}}\n <div class='recommended_item'>\n <a href=\"{{url}}\">\n <img src=\"{{thumbnail}}\" width=\"150\" height=\"150\" />\n <span class=\"title\">{{title}}</span>\n </a> by {{author}}\n </div>\n {{/items}}\n</script>", "language": "html" } ] } [/block] ***Step 3*** [block:code] { "codes": [ { "code": "// Register calls for different widgets.\n$p('register', {\n\tmax: 6, // Number of items you want to show\n\twidget: 'bottom-widget',\n\topts : {},\n\tcallback: function(resp) {\n // Query selector should match div name from Step 1\n\t\tvar el = document.querySelector('#li-bottom-recommendation-unit');\n // Template should match mustache template name from Step 2\n\t\tvar template = document.querySelector('#li-bottom-recommendation-template').innerHTML;\n // Basically Mustache.render(template, resp);\n\t\tel.innerHTML = $p('render', template, resp);\n // Remove in production\n console.log(\"Debug was set to true; disable it in production!\"); \n\t\t$p('track', {\n\t\t\telements: document.querySelectorAll('#li-bottom-recommendation-unit > div.recommended_item'), \n // Match widget name\n name: 'bottom-widget',\n // Source \"LI\" indicates recommendations are provided\n\t\t\t// by LiftIgniter\n\t\t\tsource: 'LI',\n // Optional parameters to track with each event.\n // If the corresponding 'register' call had optional\n // fields, exactly those fields MUST be present here.\n\t\t\topts: {},\n\t\t\t_debug : true\n });\n }\n});\n\n$p('register', {\n\tmax: 4, // Number of items you want to show\n widget: 'trending-widget',\n\topts : {},\n callback: function(resp) {\n // Query selector should match div name from Step 1\n\t\tvar el = document.querySelector('#li-trending-recommendation-unit');\n\t\t// Template should match mustache template name from Step 2\n var template = document.querySelector('#li-trending-recommendation-template').innerHTML;\n // Basically Mustache.render(template, resp);\n\t\tel.innerHTML = $p('render', template, resp);\n console.log(\"Debug was set to true; disable it in production!\"); // Remove in production\n\t\t$p('track', {\n // Div name from Step 1 and recommendation item div name from Step 2\n\t\t\telements: document.querySelectorAll('#li-trending-recommendation-unit > div.recommended_item'), \n // Match widget name\n name: 'trending-widget',\n // Source \"LI\" indicates recommendations are provided\n\t\t\t// by LiftIgniter\n\t\t\tsource: 'LI',\n // Optional parameters to track with each event.\n // If the corresponding 'register' call had optional\n // fields, exactly those fields MUST be present here.\n\t\t\topts: {},\n\t\t\t_debug : true\n\t\t});\n\t}\n});\n\n// Fetch recommendations and trigger callbacks for all registered.\n$p('fetch');", "language": "javascript" } ] } [/block] [block:callout] { "type": "info", "title": "Fetching", "body": "The fetch function must be called exactly once after all calls have been registered. This way we can make sure there are no duplicate recommendations across widgets, and also reduce latency by combining http calls to the model server when possible." } [/block] [block:callout] { "type": "info", "body": "A widget can be registered multiple times before the fetch. Each time a widget is registered, the previous registration is overwritten. You will see a message in the Javascript console when you re-register a widget.", "title": "Multiple widget registration" } [/block] [block:api-header] { "type": "basic", "title": "2. Priority between widgets, deduplication, and number of recommendations per widget" } [/block] If you are showing multiple widgets on the same page, the priority between the widgets is determined by the order in which you register the widgets. If a particular widget is more important to you (i.e., you want to show better recommendations for it) be sure to register it first. If you have multiple widgets, it is particularly important that you request only as many recommendations per widget as you need for that widget. Otherwise, the deduplication might lead us to discard quality recommendations. [block:api-header] { "type": "basic", "title": "3. Request Extra Fields" } [/block] We allow only one set of fields across all widgets. Thus, if you need different fields across different widgets, you should fetch all fields that are needed for any of the widgets you are registering. Note that we do not return fields for inventory items if we do not have those field values for those inventory items. [block:api-header] { "type": "basic", "title": "4. Trigger different widgets in different contexts" } [/block] You might have different widget names for different user contexts. A typical use case is that your site has different layouts for desktop, mobile, and tablet, and you have different sets of active widgets for these contexts. For instance, you may have both a bottom widget and a trending widget in desktop, but only a bottom widget in mobile. Please make sure to only register the widgets that are active in the context, so that the recommendations you fetch are the very best (if you register additional widgets that aren’t being used, you might end up getting poorer-quality recommendations on the widgets you do show because of deduplication).