From ab28d03ba00d312ac18bd59881a7a40acd1438f2 Mon Sep 17 00:00:00 2001 From: Bernardo Srulzon Date: Fri, 14 Apr 2017 16:06:59 -0300 Subject: [PATCH 1/5] tracking mutable elements clicks on forms --- core/lib/core.ts | 38 ++++++++++++++++++++++++++++++++++++++ core/tests/unit/core.js | 31 +++++++++++++++++++++++++++++++ src/js/forms.js | 13 ++++++++++--- 3 files changed, 79 insertions(+), 3 deletions(-) diff --git a/core/lib/core.ts b/core/lib/core.ts index 0362e8148..233a0f0b2 100644 --- a/core/lib/core.ts +++ b/core/lib/core.ts @@ -785,6 +785,44 @@ export function trackerCore(base64: boolean, callback?: (PayloadData) => void) { }, context, tstamp); }, + /** + * Track the action of a select form field being clicked + * + * @param formId The parent form ID + * @param elementId ID of the changed element + * @param nodeName "SELECT" + * @param type Type of the changed element if its type is "INPUT" + * @param elementClasses List of classes of the changed element + * @param value The current value of the changed element + * @param context Optional. Context relating to the event. + * @param tstamp Optional. TrackerTimestamp of the event + * @return Payload + * + * @todo make `nodeName` enum + */ + trackFormClick: function ( + formId: string, + elementId: string, + nodeName: string, + type: string, + elementClasses: Array, + value: string, + context?: Array, + tstamp?: Timestamp): PayloadData { + + return trackSelfDescribingEvent({ + schema: 'iglu:com.snowplowanalytics.snowplow/click_form/jsonschema/1-0-0', + data: removeEmptyProperties({ + formId: formId, + elementId: elementId, + nodeName: nodeName, + type: type, + elementClasses: elementClasses, + value: value + }, {value: true}) + }, context, tstamp); + }, + /** * Track the value of a form field changing * diff --git a/core/tests/unit/core.js b/core/tests/unit/core.js index d7dfacc51..7d965624f 100644 --- a/core/tests/unit/core.js +++ b/core/tests/unit/core.js @@ -400,6 +400,37 @@ define([ compare(tracker.trackRemoveFromCart(sku, name, category, unitPrice, quantity, currency), expected); }, + "Track a form click event": function () { + var formId = "parent"; + var elementId = "child"; + var nodeName = "INPUT"; + var type = "text"; + var elementClasses = ["important"]; + var value = "male"; + + var inputJson = { + schema: 'iglu:com.snowplowanalytics.snowplow/click_form/jsonschema/1-0-0', + data: { + formId: formId, + elementId: elementId, + nodeName: nodeName, + type: type, + elementClasses: elementClasses, + value: value + } + }; + + var expected = { + e: 'ue', + ue_pr: JSON.stringify({ + schema: unstructEventSchema, + data: inputJson + }) + }; + + compare(tracker.trackFormClick(formId, elementId, nodeName, type, elementClasses, value), expected); + }, + "Track a form change event": function () { var formId = "parent"; var elementId = "child"; diff --git a/src/js/forms.js b/src/js/forms.js index 11177e713..930c69e1b 100755 --- a/src/js/forms.js +++ b/src/js/forms.js @@ -119,12 +119,18 @@ object.getFormTrackingManager = function (core, trackerId, contextAdder) { /* * Return function to handle form field change event */ - function getFormChangeListener(context) { + function getFormChangeListener(type, context) { return function (e) { var elt = e.target; var type = (elt.nodeName && elt.nodeName.toUpperCase() === 'INPUT') ? elt.type : null; var value = (elt.type === 'checkbox' && !elt.checked) ? null : elt.value; - core.trackFormChange(getParentFormName(elt), getFormElementName(elt), elt.nodeName, type, helpers.getCssClasses(elt), value, contextAdder(context)); + if (type == 'change') { + core.trackFormChange(getParentFormName(elt), getFormElementName(elt), elt.nodeName, type, helpers.getCssClasses(elt), value, contextAdder(context)); + } + else if (type == 'click') { + core.trackFormClick(getParentFormName(elt), getFormElementName(elt), elt.nodeName, type, helpers.getCssClasses(elt), value, contextAdder(context)); + + } }; } @@ -162,7 +168,8 @@ object.getFormTrackingManager = function (core, trackerId, contextAdder) { lodash.forEach(innerElementTags, function (tagname) { lodash.forEach(form.getElementsByTagName(tagname), function (innerElement) { if (fieldFilter(innerElement) && !innerElement[trackingMarker]) { - helpers.addEventListener(innerElement, 'change', getFormChangeListener(context), false); + helpers.addEventListener(innerElement, 'click', getFormChangeListener('click', context), false); + helpers.addEventListener(innerElement, 'change', getFormChangeListener('change', context), false); innerElement[trackingMarker] = true; } }); From dc2dc8393c82afe3876e9c316ba762af478147ea Mon Sep 17 00:00:00 2001 From: Bernardo Srulzon Date: Sat, 15 Apr 2017 01:15:49 -0300 Subject: [PATCH 2/5] allowing a click event only once --- src/js/forms.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/js/forms.js b/src/js/forms.js index 930c69e1b..6da63a898 100755 --- a/src/js/forms.js +++ b/src/js/forms.js @@ -119,17 +119,16 @@ object.getFormTrackingManager = function (core, trackerId, contextAdder) { /* * Return function to handle form field change event */ - function getFormChangeListener(type, context) { + function getFormChangeListener(event_type, context) { return function (e) { var elt = e.target; var type = (elt.nodeName && elt.nodeName.toUpperCase() === 'INPUT') ? elt.type : null; var value = (elt.type === 'checkbox' && !elt.checked) ? null : elt.value; - if (type == 'change') { + if (event_type == 'change') { core.trackFormChange(getParentFormName(elt), getFormElementName(elt), elt.nodeName, type, helpers.getCssClasses(elt), value, contextAdder(context)); } - else if (type == 'click') { + else if (event_type == 'click') { core.trackFormClick(getParentFormName(elt), getFormElementName(elt), elt.nodeName, type, helpers.getCssClasses(elt), value, contextAdder(context)); - } }; } @@ -168,7 +167,7 @@ object.getFormTrackingManager = function (core, trackerId, contextAdder) { lodash.forEach(innerElementTags, function (tagname) { lodash.forEach(form.getElementsByTagName(tagname), function (innerElement) { if (fieldFilter(innerElement) && !innerElement[trackingMarker]) { - helpers.addEventListener(innerElement, 'click', getFormChangeListener('click', context), false); + helpers.addEventListener(innerElement, 'click', getFormChangeListener('click', context), { 'once': true }); helpers.addEventListener(innerElement, 'change', getFormChangeListener('change', context), false); innerElement[trackingMarker] = true; } From 8afd5f7d956923f45191ada2e87ca11cf219f0ea Mon Sep 17 00:00:00 2001 From: Bernardo Srulzon Date: Sun, 16 Apr 2017 17:48:59 -0300 Subject: [PATCH 3/5] click -> focus --- core/lib/core.ts | 45 ++++------------------------------------- core/tests/unit/core.js | 8 ++++---- src/js/forms.js | 11 +++------- 3 files changed, 11 insertions(+), 53 deletions(-) diff --git a/core/lib/core.ts b/core/lib/core.ts index 233a0f0b2..ad5c87e9f 100644 --- a/core/lib/core.ts +++ b/core/lib/core.ts @@ -786,45 +786,7 @@ export function trackerCore(base64: boolean, callback?: (PayloadData) => void) { }, /** - * Track the action of a select form field being clicked - * - * @param formId The parent form ID - * @param elementId ID of the changed element - * @param nodeName "SELECT" - * @param type Type of the changed element if its type is "INPUT" - * @param elementClasses List of classes of the changed element - * @param value The current value of the changed element - * @param context Optional. Context relating to the event. - * @param tstamp Optional. TrackerTimestamp of the event - * @return Payload - * - * @todo make `nodeName` enum - */ - trackFormClick: function ( - formId: string, - elementId: string, - nodeName: string, - type: string, - elementClasses: Array, - value: string, - context?: Array, - tstamp?: Timestamp): PayloadData { - - return trackSelfDescribingEvent({ - schema: 'iglu:com.snowplowanalytics.snowplow/click_form/jsonschema/1-0-0', - data: removeEmptyProperties({ - formId: formId, - elementId: elementId, - nodeName: nodeName, - type: type, - elementClasses: elementClasses, - value: value - }, {value: true}) - }, context, tstamp); - }, - - /** - * Track the value of a form field changing + * Track the value of a form field changing or receiving focus * * @param formId The parent form ID * @param elementId ID of the changed element @@ -838,7 +800,8 @@ export function trackerCore(base64: boolean, callback?: (PayloadData) => void) { * * @todo make `nodeName` enum */ - trackFormChange: function ( + trackFormFocusOrChange: function ( + schema: string, formId: string, elementId: string, nodeName: string, @@ -849,7 +812,7 @@ export function trackerCore(base64: boolean, callback?: (PayloadData) => void) { tstamp?: Timestamp): PayloadData { return trackSelfDescribingEvent({ - schema: 'iglu:com.snowplowanalytics.snowplow/change_form/jsonschema/1-0-0', + schema: 'iglu:com.snowplowanalytics.snowplow/' + schema + '/jsonschema/1-0-0', data: removeEmptyProperties({ formId: formId, elementId: elementId, diff --git a/core/tests/unit/core.js b/core/tests/unit/core.js index 7d965624f..5316e5ad7 100644 --- a/core/tests/unit/core.js +++ b/core/tests/unit/core.js @@ -400,7 +400,7 @@ define([ compare(tracker.trackRemoveFromCart(sku, name, category, unitPrice, quantity, currency), expected); }, - "Track a form click event": function () { + "Track a form focus event": function () { var formId = "parent"; var elementId = "child"; var nodeName = "INPUT"; @@ -409,7 +409,7 @@ define([ var value = "male"; var inputJson = { - schema: 'iglu:com.snowplowanalytics.snowplow/click_form/jsonschema/1-0-0', + schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0', data: { formId: formId, elementId: elementId, @@ -428,7 +428,7 @@ define([ }) }; - compare(tracker.trackFormClick(formId, elementId, nodeName, type, elementClasses, value), expected); + compare(tracker.trackFormFocusOrChange('focus_form', formId, elementId, nodeName, type, elementClasses, value), expected); }, "Track a form change event": function () { @@ -459,7 +459,7 @@ define([ }) }; - compare(tracker.trackFormChange(formId, elementId, nodeName, type, elementClasses, value), expected); + compare(tracker.trackFormFocusOrChange('change_form', formId, elementId, nodeName, type, elementClasses, value), expected); }, "Track a form submission event": function () { diff --git a/src/js/forms.js b/src/js/forms.js index 6da63a898..6d4ce0d46 100755 --- a/src/js/forms.js +++ b/src/js/forms.js @@ -124,12 +124,7 @@ object.getFormTrackingManager = function (core, trackerId, contextAdder) { var elt = e.target; var type = (elt.nodeName && elt.nodeName.toUpperCase() === 'INPUT') ? elt.type : null; var value = (elt.type === 'checkbox' && !elt.checked) ? null : elt.value; - if (event_type == 'change') { - core.trackFormChange(getParentFormName(elt), getFormElementName(elt), elt.nodeName, type, helpers.getCssClasses(elt), value, contextAdder(context)); - } - else if (event_type == 'click') { - core.trackFormClick(getParentFormName(elt), getFormElementName(elt), elt.nodeName, type, helpers.getCssClasses(elt), value, contextAdder(context)); - } + core.trackFormFocusOrChange(event_type, getParentFormName(elt), getFormElementName(elt), elt.nodeName, type, helpers.getCssClasses(elt), value, contextAdder(context)); }; } @@ -167,8 +162,8 @@ object.getFormTrackingManager = function (core, trackerId, contextAdder) { lodash.forEach(innerElementTags, function (tagname) { lodash.forEach(form.getElementsByTagName(tagname), function (innerElement) { if (fieldFilter(innerElement) && !innerElement[trackingMarker]) { - helpers.addEventListener(innerElement, 'click', getFormChangeListener('click', context), { 'once': true }); - helpers.addEventListener(innerElement, 'change', getFormChangeListener('change', context), false); + helpers.addEventListener(innerElement, 'focus', getFormChangeListener('focus_form', context), false); + helpers.addEventListener(innerElement, 'change', getFormChangeListener('change_form', context), false); innerElement[trackingMarker] = true; } }); From 7d5934115c04ae5dea2dc3c277e70049d7758087 Mon Sep 17 00:00:00 2001 From: Bernardo Srulzon Date: Sun, 16 Apr 2017 18:52:18 -0300 Subject: [PATCH 4/5] don't track focus on checkbox or radio buttons --- src/js/forms.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/js/forms.js b/src/js/forms.js index 6d4ce0d46..75618131a 100755 --- a/src/js/forms.js +++ b/src/js/forms.js @@ -124,7 +124,9 @@ object.getFormTrackingManager = function (core, trackerId, contextAdder) { var elt = e.target; var type = (elt.nodeName && elt.nodeName.toUpperCase() === 'INPUT') ? elt.type : null; var value = (elt.type === 'checkbox' && !elt.checked) ? null : elt.value; - core.trackFormFocusOrChange(event_type, getParentFormName(elt), getFormElementName(elt), elt.nodeName, type, helpers.getCssClasses(elt), value, contextAdder(context)); + if (type != 'checkbox' && type != 'radio') { + core.trackFormFocusOrChange(event_type, getParentFormName(elt), getFormElementName(elt), elt.nodeName, type, helpers.getCssClasses(elt), value, contextAdder(context)); + } }; } From 2205e2b277c970e8909406e18a2c76a9027015c0 Mon Sep 17 00:00:00 2001 From: Bernardo Srulzon Date: Sun, 16 Apr 2017 19:01:05 -0300 Subject: [PATCH 5/5] fixes tracking of change_form events --- src/js/forms.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/forms.js b/src/js/forms.js index 75618131a..e18cac1b9 100755 --- a/src/js/forms.js +++ b/src/js/forms.js @@ -124,7 +124,7 @@ object.getFormTrackingManager = function (core, trackerId, contextAdder) { var elt = e.target; var type = (elt.nodeName && elt.nodeName.toUpperCase() === 'INPUT') ? elt.type : null; var value = (elt.type === 'checkbox' && !elt.checked) ? null : elt.value; - if (type != 'checkbox' && type != 'radio') { + if (event_type === 'change_form' || (type != 'checkbox' && type != 'radio')) { core.trackFormFocusOrChange(event_type, getParentFormName(elt), getFormElementName(elt), elt.nodeName, type, helpers.getCssClasses(elt), value, contextAdder(context)); } };