-
Notifications
You must be signed in to change notification settings - Fork 44
Usage
Loaded in a webpage, genetify.js lets you create experiments in your code.
Each experiment tests a group of alternatives and chooses the best one.
Genetify.js works with groups of CSS rules, javascript variables or HTML
tags. The groups are defined implicitly by adding special suffixes like “_vBeta”
to an object’s original name.
Experiments are evaluated by user feedback. Any kind of user interaction can
be turned into a goal of an experiment by attaching to standard DOM events.
The overarching aim of genetify.js is to make the programming of code experiments as
natural as possible.
Groups of CSS rules, javascript variables or HTML tags whose members are meant
to be tested on users will be called genes, inspired by the unit of
selection in biological evolution.
The members of those groups will be called variants because they should each
vary one from the other in some purposeful way.
To say that something is genetified is to say that it has been branched off
into multiple variants and is ready to be tested.
Insert a style tag into the head of a HTML document.
<style type="text/css" media="all"> .v { display: none; } .genetify_disabled { display: none !important; } .genetify_enabled { display: block !important; } </style>
These CSS rules are important because they ensure that variants are not
displayed if genetify.js fails to load or execute.
Below the style tag, insert a script tag referencing genetify.js.
<script type="text/javascript" src="http://app.genetify.com/genetify.js"></script>
Genetify.js must be loaded before any other javascript upon which it is
supposed to act, so typically it should be inserted as the first script tag in
the head. As a small optimization, you could put it below any scripts you know
will not be genetified.
At the bottom of the page, just before the </body>
tag, insert a script tag
that calls the main function of genetify.
<script type="text/javascript"> genetify.vary(); </script>
This is the fun part. To make a new gene, you simply branch off some existing
object and add a suffix to the new object. It is easiest to learn by example:
#navbar { color: red; } #navbar_vA { color: green; }
The original CSS rule here can be branched off infinitely.
#navbar { color: red; } #navbar_vA { color: green; } ... #navbar_vZZZ { color: chartreuse; }
The suffix can take one of several forms, depending on the type of code of the
gene being defined.
As seen above, CSS rules take a underscore “_” followed by “v” and the variant
name. The other type of CSS gene, an additive rule, takes a “.” instead of a
“_”, as in:
#navbar { color: red; } #navbar.vGreen { background-color: green; }
The functional differences between these two types are explained in the
section How genes are varied.
Javascript variables are genetified in the same way as CSS Rules. For example:
highlight = function(elem){ elem.style.borderColor = 'green'; } highlight_vRed = function(elem){ elem.style.borderColor = 'red'; }
HTML elements are genetified like this:
<div class="sentence">One way of saying something</div> <div class="sentence v anotherway">Another way of saying something</div>
The reason for the separate “v” class is because HTML elements have an effect
on the user by default (by being shown on screen) unlike an unused chunk of
javascript or CSS. The “v” class lets genetify hide variants until they are
switched on. A class name to the left of “v”, “sentence” in this example, must
be present to group elements together into a gene. Multiple genes can then
coexist on the same page.
<div class="sentence">One way of saying something</div> <div class="sentence v anotherway">Another way of saying something</div> <div class="sentence2">Qu'est-ce qu'il dit?</div> <div class="sentence2 v anotherway">Je ne sais pas.</div>
The common naming scheme across all types of genes is:
GeneName + VariantMarker + VariantName
On an abstract level, genes comprised of CSS rules and javascript variables
are handled the same way. One of the variants (including the original) is
randomly chosen and the original name is then reassigned to the chosen
variant. This has the effect of putting the variant object in the place of the
original object wherever the original object is referenced.
Genes that are comprised of additive CSS rules are handled somewhat
differently. Instead of an original CSS rule being overwritten, the chosen
variant is added as a class to HTML elements that match the rest of the
selector. Take this simplified document as an example.
#navbar { color: red; } #navbar.vGreen { background-color: green; } <div id="navbar">lalalalala</div>
Assuming variant Green is chosen, “vGreen” will be added to the className property of
element navbar, causing it to inherit the background-color specified in the
rule. The advantage of additive CSS genes over plain CSS genes is that
variants need only contain the difference between them and the original CSS
rule. With plain CSS rules, the same effect would be achieved by:
#navbar { color: red; } #navbar_vGreen { color: red; background-color: green; }
Finally, element genes are varied by adding the special CSS classes
“genetify_enabled” or “genetify_disabled” to variants. As explained above,
properly named element genes have all variants hidden to start with except the
designated default.
Note that applying “genetify_enabled” to elements whose default display
property is not “block” may cause trouble. Read below for a simple workaround.
This is the other fun part. To genetify.js, a goal is the calling of a
special function, genetify.record.goal, that sends a request to the genetify
server. The request contains information on the goal and gene variants that
have been selected on the site. Each goal must be given a name and a value.
genetify.record.goal('nirvana', 100);
Typically, you’ll want to call genetify.record.goal after a DOM event. For
instance, as the onclick handler of a link.
<a href="#" onmousedown="genetify.record.goal('score', 2);">score!</a>
Another common case is a call at the bottom of a page in order to record a
pageview.
<script type="text/javascript"> genetify.record.goal('pageview', 1); </script>
The name of a goal can be any string less than 255 characters. The value of a
goal can be any positive number. The value affects the relative weight of a
goal when multiple goals exist on a single site.
The point of recording goals is to influence the selection of variants. As
explained above, recording a goal sends information to a remote server. The
remote server aggregates such information and calculates which variants led to
which goals.
Results come back to a genetified page when it loads. When genetify.js
initializes, it makes a request for a JSON file keyed on the current domain
and page. The JSON file contains information on each variant in the page and
its history of producing goals.
When a gene is varied, genetify.js checks the fetched results for information
about that gene’s variants. When present, this information influences the
probability that each variant is selected. Variants that have led to more
goals in the past will be selected more frequently — variants that have led
to fewer goals will be selected less frequently. In this way, genes adapt to
user feedback.
Genetify tracks several metrics for the variants of each gene:
- count – the number of times that variant was used
- sum – the value of the goals accomplished when that variant was shown
- avg – the expected value of showing that variant
- weight – the percentage chance that variant will be shown
At any point you can view the performance of the variants on a particular page by pressing “CTRL-G” or setting LOAD_CONTROLS to True (see below).
Here is a useful sequence for testing genetify.js on a page.
1. Check for javascript errors. Genetify.js performs checks on load.
2. Set GENETIFY_CONFIG = { USE_COOKIE: false, LOAD_CONTROLS: true } . Read
about configuration options below.
3. Add GeneName + VariantMarker + VariantName markup to a minimal subset of
things you want to vary.
4. Call genetify.vary or use control panel and check for errors.
5. Observe all your expected variants by repeatedly varying the page. Note
that by default genetify will maintain state for a single visitor (USE_COOKIE
= true).
6. Add a goal (or use control panel) and call it repeatedly for one variant of
a gene. You should observe a change in frequency of that variant over page
loads.
7. Add GeneName + VariantMarker + VariantName markup to the remaining things
you want to vary.
Genetify.js will read in certain configuration options in the global variable
“GENETIFY_CONFIG” when it first loads. Names and descriptions are below.
Defaults are values shown.
GENETIFY_CONFIG = { // Request remote script that contains results for variants in the // current page. REQUEST_RESULTS: true, // Request results as a static file from a cache on the server. USE_RESULTS_CACHE: true, // Force current genes to variants previously viewed by visitor. // Useful for ensuring a consistent user experience. USE_COOKIE: true, // Namespace in which encountered genes are placed. Useful for // treating genes on different pages as a single unit. NAMESPACE: window.location.pathname, // Skip all initialization for varying genes. Useful on pages with // goals but no genes. NO_VARYING: false, // Load graphical control panel for testing. LOAD_CONTROLS: false }
Goals can be recorded on any page, even one without genes. Information about
genes on a page is written into a cookie when genetify.vary is called. When
genetify.record.goal is subsequently called anywhere on the same domain, the
information in the cookie will be read out.
To ensure that this works as intended, you should set two of the config vars.
GENETIFY_CONFIG = { NAMESPACE: document.domain, // or some constant across pages of interest USE_COOKIE: true // so the cookie containing the genome will be set }
You may want to target an experiment to one kind of visitor to your site. Or you may want to run genetify on a fraction of pageviews in order to be conservative. The easiest way to run genetify on some condition is to wrap genetify.vary in an if statement.
if (condition) genetify.vary();
Then you will also need to check for pageview_xid before recording a goal or else genetify will think something has gone wrong.
if (genetify.pageview_xid) genetify.record.goal('success', 1);
The special CSS class “genetify_enabled” specifies the display mode of
genetified HTML elements throughout the page. By default, it is set to
“display: block”. If you want to display genetified elements as “inline” or in
some other mode, you can simply change the “genetify_enabled” rule.
.genetify_enabled { display: inline; }
This would cause selected variants to display in “inline” mode. Alternatively,
you could leave the default as “block” and make only spans display “inline”.
.genetify_enabled { display: block; } span.genetify_enabled { display: inline; }
Genetify allows multiple elements to switched on and off as a single variant.
A group of elements are denoted as a single variant in a natural way:
<div class="emotion v happy"> :-D </div> <div class="emotion v sad"> :-( </div> <div class="emotion v happy"> I'm happy </div> <div class="emotion v sad"> I'm sad </div>
In this example, the happy and sad emoticons will be paired with the
appropriate text. Note that the other types of variants — additive CSS rules,
CSS rules and javascript — may affect multiple HTML elements by their
inherent operation.
It is possible to call genetify.vary for only a specific type of gene. This
might be useful for optimizing peformance or avoiding buggy interactions. The
calls are:
genetify.vary('javascript'); genetify.vary('CSSRules'); genetify.vary('elements'); genetify.vary('additiveCSSRules');
It is also possible to call for multiple types.
genetify.vary('javascript', 'CSSRules');
Advanced users should know that it is possible to successfully call
genetify.vary from different stages of a normal page load. The important point
is that all the code that genetify is to operate on has already loaded.
The specific rules to follow are:
// Must come before all script tags that load genetified javascript, // likely somewhere in the head genetify.vary('javascript'); // Must come after all genetified CSS rules have been declared, likely at // the bottom of the head genetify.vary('CSSRules'); // Must come after all genetified elements, likely at the bottom of page // or after onload event has fired genetify.vary('elements'); // Must come after all elements referred to by genetified CSS rules, // likely at the bottom of page or after onload event has fired genetify.vary('additiveCSSRules');
At times it may even be desirable to call genetify.vary outside of a normal
page load, maybe after an AJAX request. The function should always act on
whatever genes are accessible when called, but switching variants after
default variants have lingered onscreen may surprise the user.
Because of the immediate rendering of content by browsers, calling
genetify.vary late in the page load process can cause an apparent flicker
onscreen as the original variant of a gene is switched for another variant. A
simple and effective solution to this problem is to put the call to
genetify.vary as early as possible. For example:
<div class="sentence">One way of saying something</div> <div class="sentence v anotherway">Another way of saying something</div> <script type="text/javascript"> genetify.vary(); </script>
With all the scanning of namespaces, it is fair to ask how genetify affects
page load performance. Based on real world data across all browsers,
initializing genetify.js takes on average about 70 miliseconds, while
executing genetify.vary takes on average 30ms.
Performance depends most strongly on the size of the underlying namespaces
being searched — the numbers of CSS rules, HTML elements or javascript
objects. In most cases, performance can be improved substantially by
restricting the search to only those namespaces that are known to contain
genes. See the section “Calling at will” above. On pages where no genes exist
(typically when genetify.js is needed only to record a goal) most
initialization time can be skipped by specifiying “NO_VARYING” as a
configuration option.
Genetify.vary does not work the same way for external javascript in Internet
Explorer.
It seems that although IE registers global variables in the window object as
it should, it does not include them in the window object when iterating over
it, so genetify can’t necessarily access all javascript variables. As a
workaround, it scans the contents of all script tags on the page for variable
names, and then it traverses the namespaces of what it finds.
The takeaway message is that a genetified javascript variable or one of its
parents must be referenced in the contents of a script tag in the current page
for it to be found. For example:
<script type="text/javascript" src="mystuff.js"></script>
Nothing in mystuff.js will be found unless somewhere on the same page is
written a reference to some object from mystuff.js:
<script type="text/javascript"> mystuff.dosomestuff(); </script>
If “mystuff” is the root object to everything in mystuff.js, then
genetify.vary will work perfectly in IE.
Genetify has been tested and runs well in other major browsers.