Facebook event tracking for button click on squarespace - javascript

I'm installing a FB event tracking pixel to load when a button is clicked on this page: https://www.naiik.com/booking/
I found this earlier post, but the event is not loading for me: Add FB pixel event code into button - Squarespace
I've specifically injected this code:
<script>
(function() {
var btns = document.getElementsByClassName("sqs-block-button-element");
btns.addEventListener("click", function() {
fbq("track", "Lead");
});
})();
</script>
I injected it in the page specific header.
I'm using Facebook's Pixel Helper chrome extension and it keeps saying that this pixel has not loaded. All the while, the main FB pixel, which I installed previously, is working just fine.
Thank you!

There are 2 errors in your code.
1. Invalid setEventListener() method usage
When you call document.getElementsByClassName('class') the resulting value is a special HTMLCollection array of all elements with a given class that were found in the DOM. (Notice the plural spelling of the function name — elements.)
What to do next:
To achieve the desired result you can change your code to enumerate over the list of results returned by .getElementsByClassName; to do that, use a simple for loop on your result.
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener("click", function() {
fbq("track", "Lead");
});
}
2. Incorrect selector for Squarespace button classes
If you will call the selector and inspect the resulting HTMLCollection array or directly inspect the buttons on the page you will see that button blocks that Squarespace uses have the class .sqs-block-button-element. It makes sense: after all these are Button Blocks you have placed in your page editor. The Form Block, on the other hand, does not register it's Submit button with that class (it's a .button.sqs-system-button.sqs-editable-button).
What to do next:
To achieve correct Facebook Pixel installation you need to register it on the meaningful marketing event emitters. Unfortunately, there is no one commonly used class for the majority of Squarespace buttons.
You could keep it simple and just register the Form Block .button in your addEventListener code. This would have an unfortunate consequence of not catching clicks to other buttons.
You also could target both the Button Block and Form Block Buttons, add them to a single Array object and then attach a listener to each one of them.
But there is a better option.
Use the necessity of targeting different types of buttons to indicate different Pixel properties.
E.g. the Form Block button will generate a Form-specific Pixel event, Button block — Button-specific Pixel event:
Example:
var blockBtns = document.getElementsByClassName("sqs-block-button-element");
var formBtns = document.getElementsByClassName("button");
Your new code:
<script>
(function() {
var blockBtns = document.getElementsByClassName("sqs-block-button-element");
var formBtns = document.getElementsByClassName("button");
for (var i = 0; i < blockBtns.length; i++) blockBtns[i].addEventListener("click", function() {
fbq("track", "Lead", {
eventSource: "Button Block"
});
});
for (var i = 0; i < formBtns.length; i++) formBtns[i].addEventListener("click", function() {
fbq("track", "Lead", {
eventSource: "Form Block"
});
});
})();
</script>
You can refactor it as you wish and/or use the Squarespace-native YUI library to improve and stabilize the code.
Additional recommendations:
Most likely the Pixel Extension error is due to your AdBlock extension. You must disable AdBlock on your domain and on your .squarespace.com internal subdomain to develop with Pixel and other marketing tools.
If you initialize your pixel with advanced customer matching — the em variable in your Pixel init() call — you need to correctly pass the customer data. That's a very powerful marketing technique and you should research what's possible with Squarespace (not easy).
Use fbq('track', 'PageView'); to track visitors coming to your lead-generating pages. Not every user clicks a button to call or submit a form, but every user that chooses to view a "Subscription" or "Contact" page is a prospective customer.

Related

Move entire document into iframe

I'm adding a chat feature to a couple of our websites. The chat will connect users with people at our help desk to help them use the websites. Our help desk folks want the chat window to appear like a tab on the side of the page and slide out, rather than popping up in a new window. However, I want to allow the user to navigate around the site without losing the chat.
To do this, I've been trying to move the entire page into an iframe once the chat starts (with the chat outside the iframe), so the user can navigate around the site within the iframe without losing the chat.
I used this answer to get started, and that works great visually. However, some of the javascript in the background breaks.
One of the sites is ASP.NET web forms. The other is MVC. I've been working with the web forms one first. Stuff like calling __doPostBack breaks once the page is moved into the iframe since the javascript context is left behind.
Once the user clicks on a link (a real link, not a __doPostBack) and the iframe refreshes, then everything works perfectly.
How I see it, I have a few options:
Copy all javascript variables from window.top into the iframe somehow. Hopefully without having to know all the variable names. I tried this.contentWindow.__doPostBack = window.top.__doPostBack, which works, but other variables are missing so it ultimately fails:
Somehow switch the iframe's context to look at the top window context, if that's even possible? Probably not.
Another thought was to not move the page into an iframe right away, but to wait until the page changes and then load the new page into a new iframe. But I'm not sure how to hook into that event and highjack it.
Something else?
These are sites for use by our employees only, so I only have to support IE11 and Chrome.
Update:
Thanks to LGSon for putting me on the track of using the target attribute (so I can use approach #3). Below is what I ended up doing. When I pop out the chat, I call loadNextPageInIframe(). I'm using jQuery here since we already use it on our site, but everything could be done without. I set the target on all links that don't already have a target pointing to another frame or _blank. I left _parent out, but I don't think we use it anyway.
I have a reference to my chat window div in a global variable called 'chatwindow'.
There still could be some cases where this doesn't work, such as if there is some javascript that sets window.location directly. If we have anything in our sites that does this, I'll have to add a way to handle it.
function loadNextPageInIframe() {
var frame = $("<iframe id=\"mainframe\" name=\"mainframe\" />").css({
position: "fixed",
top: 0,
left: 0,
width: "100%",
height: "100%",
border: "none",
display: "none"
}).appendTo("body");
$("form, a:not([target]), a[target=''], a[target='_top'], a[target='_self']").attr("target", "mainframe");
var firstload = true;
frame.load(function () {
//Runs every time a new page in the iframe loads
if (firstload) {
$(frame).show();
//Remove all elements from the top window except the iframe and chat window
$("body").children().not(frame).not(window.top.chatwindow).remove();
firstload = false;
}
//Make the browser URL and title reflect the iframe every time it loads a new page
if (this.contentWindow && this.contentWindow.location.href && window.top.location.hostname === this.contentWindow.location.hostname && window.top.location.href !== this.contentWindow.location.href) {
var title = this.contentDocument.title;
document.title = title;
if (window.top.history.replaceState) window.top.history.replaceState(null, title, this.contentWindow.location.href);
}
});
}
May I suggest you do the following
get all links
attach an event click handler to intercept when someone click a link
on click event, check if chat is in progress, and if, feed the iframe with the new link
var links = querySelectorAll("a");
for (var i = 0; i < links.length; i++) {
links[i].addEventListener('click', function(e) {
if (isChatInProgress()) {
e.preventDefault(); //stop the default action
document.getElementById("your_iframe_id").src = e.target.href;
// anything else here, like toggle tabs etc
}
});
}
Update
To handle forms I see 4 ways at the moment
1) Add an onsubmit handler to your forms
function formIsSubmitted(frm) {
if (isChatInProgress()) {
frm.target = "the iframe";
}
return true;
}
<form id="form1" runat="server" onsubmit="return formIsSubmitted(this)">
2) Add a click handler to your buttons
function btnClick(btn) {
if (isChatInProgress()) {
btn.form.target = "the iframe";
}
return true;
}
<asp:Button runat="server" ID="ButtonID" Text="ButtonText"
OnClick="Button_Click" OnClientClick="return btnClick(this);" />
3) When a chat start, you iterate through each form and alter its target attribute
function startChat() {
var forms = querySelectorAll("form");
for (var i = 0; i < forms.length; i++) {
forms[i].target = "the iframe";
});
}
4) Override the original postback event (Src: intercept-postback-event)
// get reference to original postback method before we override it
var __doPostBackOriginal = __doPostBack;
// override
__doPostBack = function (eventTarget, eventArgument) {
if (isChatInProgress()) {
theForm.target = "the iframe";
}
// call original postback
__doPostBackOriginal.call(this, eventTarget, eventArgument);
}
Update 2
The absolute best way to deal with this is of course to use AJAX to load both page and chat content, but that likely means a quite bigger work load if your sites aren't already use it.
If you don't want to do that, you can still use AJAX for chat and if a user were to navigate to a new page, the chat app recreate the ongoing chat window with all its content again.
I suggest instead of loading content to and from iframes - build the chat as an iframe and use a jQuery modal popup on the page for chat.
You can fix the jquery modal to a fixed location and page scrolling is enabled by default. You need to modify css accordingly to make the popup remains on the same location.
If you go down your current path - you will need to worry a lot about how content is moved to the iframe and it might be difficult to re-use the chat on different pages depending on the content. For example, imagine you playing a video on the page and the user clicks chat - if you load the content to the iframe - the user will lose the status on how far he has viewed, etc.
as per my opinion, adding the whole website as an 'I-Frame' is not a good design practice, and not a good solution for the problem. My suggestion would be:
Ensure that the 'Chat' application is loaded in all the pages, across your website
Whenever the 'Chat' is started, either establish the 'web-socket' connection or somehow, maintain the State on the Server
Have the configuration of the 'Chat' as 'Minimized', 'Open' etc and store them in your cookie or session storage
On every page load, call the 'Chat' application too. Read the Chat related configuration from sessionstorage or cookie and maintain it's state as 'Minimized' or 'Open' etc, including the X and Y position, if you want to make it as 'Floated'
Every time, either fetch the entire conversation from the server via Ajax or try to store and fetch from 'Local Storage' and do Ajax only for any Updates from the other party
Use CSS based 'Float' related properties to make it float and sit at some side.
This will ensure that your chat is available for the user and yet he can navigate all through the site.

Is there an event or another way to call a Javascript function when a Django Admin Popup (the green plus icon) completes?

Let's imagine we have those Django models:
class Band(models.Model):
name = models.CharField(max_length=256, default="Eagles of Death Metal")
class Song(models.Model):
band = models.ForeignKey(Band)
When using the admin to manage those models, the band field is associated to a Widget rendered by Django as a select html element.
Django's admin also adds a green plus icon next to the select, clicking it opens a pop-up window where the user is presented with the Form to add a new band. When clicking the save button in this pop-up window, the new band name is saved in the DB, and automatically assigned to the select value.
We rely on some javascript to be run each time a select value changes. It is currently listening to the change event of said element, which works fine when the user clicks a value directly in the menu proposed by the select.
Sadly, when this select is populated through the Admin Popup functionality, it seems the change event is not fired for the select, as our callback is not executed, even though the element's value is actually changed.
Is there another event we can listen to to get the same behaviour than when the user clicks a value directly from the list ?
Here is a Javascript snippet with the hackery we use to trigger a change event when the Django admin's add/change popup window is dismissed.
We use this with Django 1.7, so it works for this version at least.
Monkey-patching the Django admin's JS methods to achieve this isn't a very elegant way to do the job, but it was the least intrusive option we found. If anyone knows of a better way, let us all know.
/*
* Trigger change events when Django admin's popup window is dismissed
*/
(function($) {
$(document).ready(function() {
// HACK to override `dismissRelatedLookupPopup()` and
// `dismissAddAnotherPopup()` in Django's RelatedObjectLookups.js to
// trigger change event when an ID is selected or added via popup.
function triggerChangeOnField(win, chosenId) {
var name = windowname_to_id(win.name);
var elem = document.getElementById(name);
$(elem).change();
}
window.ORIGINAL_dismissRelatedLookupPopup = window.dismissRelatedLookupPopup
window.dismissRelatedLookupPopup = function(win, chosenId) {
ORIGINAL_dismissRelatedLookupPopup(win, chosenId);
triggerChangeOnField(win, chosenId);
}
window.ORIGINAL_dismissAddAnotherPopup = window.dismissAddAnotherPopup
window.dismissAddAnotherPopup = function(win, chosenId) {
ORIGINAL_dismissAddAnotherPopup(win, chosenId);
triggerChangeOnField(win, chosenId);
}
});
})(jQuery);

Google Analytics does not track link click events

I am currently using analytics.js (the newer version of GA) and I am trying to track all types of events from my website, including when a user clicks on an anchor tag pointing to an external URL.
I am currently using this setup:
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-KEY-HERE', { 'alwaysSendReferrer': true, 'allowAnchor': true });
and I send the events when hitting a link like this:
$(document).on("click", ".anchor-class", function (event) {
label = //get data from anchor element here ...
ga('send', 'event', 'Link Clicked', 'Click Details', label);;
return;
}
});
and this does not send anything to GA (even though the event handler is calling the ga(send...) method).
How ever, if I use this exact technique for but with event.preventDefault(); at the beginning of the function, the event is sent and it appears in GA dashboard.
Is there some setting that I missed in order to make this work properly?
Use the hitCallback function:
$(document).on('click','a', function(event){
event.preventDefault();
var label = $(this).attr('href');
ga('send', 'event', 'Link Clicked', 'Click Details', label, {
'hitCallback': function(){
window.location.href = label;
}
});
});
As pointed out by Blexy, the correct way to do this is to use a hit callback. However, you also need to take into account that users may block Google Analytics using some privacy protection tool such as Ghostery, in which case the hit callback will never be executed. The following article explains how to implement this correctly:
http://veithen.github.io/2015/01/24/outbound-link-tracking.html
We currently had this issue and ported our analytics code off of the website and into GTM. Another issue is that we have hundreds of sites that cannot have the new code released to them to deprecate the on-page analytics, but we already had GTM on them.
We were able to find the jQuery events that were bound to the click event and write code in GTM that removes the jQuery event on those clicked buttons via the exact event handler. Then we were able to apply standard GTM click triggers and tags so we didn't get double eventing.
Assuming you can easily remove the code from the page the following should work great using GTM.
This will fire an event off to analytics when a user clicks a given element and it will make any navigation wait until the corresponding tags have finished firing first before letting the page navigate away.
GTM Implementation
This is the quick and standard way now with the newer GTM.
Pre-Setup
Variables
You will need access to the Click Element Built-In variable. You can enable this under Variables --> Configure --> Click Element.
Also enable the Page Path or Page URL Built-In variable. This is later used to help you determine which pages to run the trigger on.
It looks like you're trying to get some text off of the clicked element for the event. If so you should create a Custom JavaScript variable. This just gets the inner text of the element, but you could also get an attribute or whatever other data you're looking for at this step.
Name: Click Element - Inner Text
Variable Type: Custom JavaScript
Custom JavaScript: function() { return {{Click Element}}.innerText; }
Triggers
Create a new trigger
Trigger Type: Click - Just Links
Wait for Tags: Enable this, this is the magic you're looking for.
Max wait time: set this to what you feel is appropriate and if you can live with the possibility that you may loose some analytics for a better user experience. Imagine what the wait time may be for a 3G user on a mobile device. Is 5000ms enough, not enough, maybe 10000ms. The user probably understands their connection is bad.
Enable this trigger when all of these conditions are true:
NOTE: you should only run this on pages that you need to run it on. Google isn't very clear if there is a performance loss and their "Learn More" says nothing about it.
If you need this to run on all pages though, configure it like so:
Page Path matches RegEx .*
Otherwise you should write something like:
Page Path matches RegEx ^/path/my-page$ specific page
Page Path matches RegEx ^/path/my-page/.*
NOTE: I'm not sure if those regex are correct, I'm not sure if you'll get a path with a proceeding or appended forward slash / or if you need anchors - normally it's best to be explicit on anchors so you don't get any funny business.
This trigger fires on: Some Link Clicks
Fire this trigger when an Event occurs and all of these conditions are true. Choose one below that fits your needs and change it how you need it.
Click Element matches CSS selector a
or maybe something more specific?
Click Element matches CSS selector .container .calls-to-action a
maybe only on external links? Assuming all internal links are relevant pathing.
Click Element matches CSS selector a[href^="http"]
Tags
Create a new tag
Tag Type: Event
Category: Link Clicked
Action: Click Details
Label: {{Click Element - Inner Text}}
Google Analytics Settings: best to use these for reusability and consistency rather than manually setting them up.
Firing Triggers: The Link Clicking Trigger you created above.

JavaScript: Reload page AFTER function has finished executing in infinite loop

I am creating a Google Chrome extension. This extension uses JavaScript to click all instances of a "follow" button on a specific web page, reloads the page, then performs the function again, in an infinite loop. It finds these follow buttons by fetching an attribute where the attribute "class" = "follow".
I am having trouble getting it to work properly, though. Instead of clicking all instances of the follow button and then refreshing, it just refreshes after one instance has been clicked.
I have tried removing the timer completely. That resulted in the script not working at all.
I removed the page refresh, and the script worked as expected, but obviously only for one instance of the page load.
Here is my code:
function tr_f() {
var followlinks = [];
for (var i=0; i < document.links.length; i++) {
if (document.links[i].getAttribute("class")=="follow"){
followlinks[followlinks.length] = document.links[i];
}
}
for (var i=0; i<followlinks.length; i++) {
var rrr=followlinks[i].onclick();
}
window.setTimeout( function(){document.location.reload();}, 5000 );
} tr_f();
There is also a button on this webpage with the "reload" class that reloads the page. Perhaps this could be implemented within the function and is only clicked once all instances of the "follow" button have been clicked, to make it work as expected. For informational purposes, we assume that the number of follow buttons on the page in question is unknown.
Any and all help regarding this will be greatly appreciated.

iFrame Validation

I currently have an iframe within my main page that has a number of checkboxes that need to be actioned prior to leaving the iframe. i.e, if the user commences checking the checkboxes and half way through they then click back on the main page, i.e leaving the iframe, I would like to be able to trap/validate that they have left the iframe and prompt them with a message indicating this, say with a "Note: You will lose all data entered here - Leave: Yes/No?" type message.
Prompting a User to Save When Leaving a Page. This 4guys article sounds like what you need. It talks about the onbeforeunload event. There's some awesome posts here on stackoverflow about onbeforeunload too.
It appears that onbeforeunload indeed does not fire for an iframe. Bugger!
Here's some sample code that should work though. This will only work if you're in the same domain, otherwise same origin policy will prevent the iframe from talking back to the parent.
I also haven't tested these in many browsers so YMMV.
You've got two options here, depending on where you want to put the prompt for changes logic.
Option one involves the iframe telling the parent window when there's changes.
Parent window javascript:
window.onbeforeunload=closeIt;
var changes = false;
function closeIt()
{
if (changes)
{
return "Yo, changes, save 'em?";
}
}
function somethingChanged() {
changes=true;
};
Iframe javascript:
$(function() {
$('input').change(parent.somethingChanged);
});
Option two involves the iframe taking control over the parent window's onbeforeunload
Parent window javascript:
There is none :-)
Iframe javascript:
$(function() {
parent.window.onbeforeunload = myCloseIt;
$('input').change(somethingChanged);
});
var changes = false;
function myCloseIt()
{
if (changes)
{
return "Yo, changes, save 'em?";
}
}
function somethingChanged() {
changes=true;
};
In either option the naive changes variable could be beefed up a bit, probably using techniques from the 4guys article, to see if there's really been any changes.
If they're on different domains, but you're still in charge of "both sides" of the HTML, there's still some options, they're just harder.
xssinterface is a library that uses postMessage and location hashes and secret voodoo black magic to communicate cross site.

Categories

Resources