my website has Turbolinks.
I need to inject a javascript TAG from Nielsen.
This is the code
<script>
!function(t,n){t[n]=t[n]||{nlsQ:function(e,o,c,r,s,i){return s=t.document,r=s.createElement("script"),r.async=1,r.src=("http:"===t.location.protocol?"http:":"https:")+"//cdn-gl.imrworldwide.com/conf/"+e+".js#name="+o+"&ns="+n,i=s.getElementsByTagName("script")[0],i.parentNode.insertBefore(r,i),t[n][o]=t[n][o]||{g:c||{},ggPM:function(e,c,r,s,i){(t[n][o].q=t[n][o].q||[]).push([e,c,r,s,i])}},t[n][o]}}}(window,"NOLBUNDLE");
var nSdkInstance = NOLBUNDLE.nlsQ("P38E2BEBD-E82F-4F94-9BA9-1A4EC618F5AF","nlsnInstance");
var nielsenMetadata = {
type: 'static',
assetid: "<%=hashed_url(url_for(only_path: true))%>",
section: 'D_BRW'
};
nSdkInstance.ggPM("staticstart", nielsenMetadata);
</script>
Basically the response from Nielsen is that tag is not implemented in the right way because when I change section of my website the instance is initialized again.
I think the issue are Turbolinks but I cannot disable them...
Any hint?
Related
Is there a way I can wrap an external JS script embed with lazy-load behavior to only execute when the embed is in the viewport?
Context: I have an external javascript embed that when run, generates an iframe with a scheduling widget. Works pretty well, except that when the script executes, it steals focus and scrolls you down to the widget when it’s done executing. The vendor has been looking at a fix for a couple weeks, but it’s messing up my pages. I otherwise like the vendor.
Javascript embed call:
<a href=https://10to8.com/book/zgdmlguizqqyrsxvzo/ id="TTE-871dab0c-4011-4293-bee3-7aabab857cfd" target="_blank">See
Online Booking Page</a>
<script src=https://d3saea0ftg7bjt.cloudfront.net/embed/js/embed.min.js> </script> <script>
window.TTE.init({
targetDivId: "TTE-871dab0c-4011-4293-bee3-7aabab857cfd",
uuid: "871dab0c-4011-4293-bee3-7aabab857cfd",
service: 1158717
});
</script>
While I'm waiting for the vendor to fix their js, I wondered if lazy-loading the JS embed may practically eliminate the poor user experience. Warning: I'm a JS/webdev noob, so probably can't do anything complicated. A timer-based workaround is not ideal because users may still be looking at other parts of the page when the timer runs out. Here are the things I’ve tried and what happens:
I tried:
What happened:
Add async to one or both of the script declarations above
Either only shows the link or keeps stealing focus.
Adding type=”module” to one or both script declarations above
Only rendered the link.
Wrapping the above code in an iframe with the appropriate lazy-loading tags
When I tried, it rendered a blank space.
Also, I realize it's basically the same question as this, but it didn't get any workable answers.
I actually also speak french but I'll reply in english for everybody.
Your question was quite interesting because I also wanted to try out some lazy loading so I had a play on Codepen with your example (using your booking id).
I used the appear.js library because I didn't really want to spend time trying some other APIs (perhaps lighter so to take in consideration).
The main JS part I wrote is like this:
// The code to init the appear.js lib and add our logic for the booking links.
(function(){
// Perhaps these constants could be put in the generated HTML. I don't really know
// where they come from but they seem to be related to an account.
const VENDOR_LIB_SRC = "https://d3saea0ftg7bjt.cloudfront.net/embed/js/embed.min.js";
const UUID = "871dab0c-4011-4293-bee3-7aabab857cfd";
const SERVICE = 1158717;
let vendorLibLoaded = false; // Just to avoid loading several times the vendor's lib.
appear({
elements: function() {
return document.querySelectorAll('a.booking-link');
},
appear: function(bookingLink) {
console.log('booking link is visible', bookingLink);
/**
* A function which we'll be able to execute once the vendor's
* script has been loaded or later when we see other booking links
* in the page.
*/
function initBookingLink(bookingLink) {
window.TTE.init({
targetDivId: bookingLink.getAttribute('id'),
uuid: UUID,
service: SERVICE
});
}
if (!vendorLibLoaded) {
// Load the vendor's JS and once it's loaded then init the link.
let script = document.createElement('script');
script.onload = function() {
vendorLibLoaded = true;
initBookingLink(bookingLink);
};
script.src = VENDOR_LIB_SRC;
document.head.appendChild(script);
} else {
initBookingLink(bookingLink);
}
},
reappear: false
});
})();
I let you try my codepen here: https://codepen.io/patacra/pen/gOmaKev?editors=1111
Tell me when to delete it if it contains sensitive data!
Kind regards,
Patrick
This method will Lazy Load HTML Elements only when it is visible to User, If the Element is not scrolled into viewport it will not be loaded, it works like Lazy Loading an Image.
Add LazyHTML script to Head.
<script async src="https://cdn.jsdelivr.net/npm/lazyhtml#1.0.0/dist/lazyhtml.min.js" crossorigin="anonymous" debug></script>
Wrap Element in LazyHTML Wrapper.
<div class="lazyhtml" data-lazyhtml onvisible>
<script type="text/lazyhtml">
<!--
<a href=https://10to8.com/book/zgdmlguizqqyrsxvzo/ id="TTE-871dab0c-4011-4293-bee3-7aabab857cfd" target="_blank">See
Online Booking Page</a>
<script src=https://d3saea0ftg7bjt.cloudfront.net/embed/js/embed.min.js>
</script>
<script>
window.TTE.init({
targetDivId: "TTE-871dab0c-4011-4293-bee3-7aabab857cfd",
uuid: "871dab0c-4011-4293-bee3-7aabab857cfd",
service: 1158717
});
</script>
-->
</script>
</div>
I have an app where Im using a lot of unobtrusive javascript to append rails partials to my page.
The trouble I have is this: javascript if placed in the assets directory wont work.
In Chrome dev tools I can see its loaded but, nothing happens. In my Coffeescript file I've used the DOM ready alternative for turbolinks found here. But this has no effect.
Whats strange is: If I place the script within script tags on the loaded partial then it works.
For example: this coffeescript file wont function if the partial containing the tabs is loaded after the javascript is loaded.
var ready;
ready = function() {
$('tab').click ->
$('.tab').removeClass("active");
$(this).addClass("active");
panel = $(this).attr("data-panel")
$('.panel').hide();
$('#' + panel).show();
};
$(document).ready(ready);
$(document).on('page:load', ready);
Whereas this script placed at the bottom of the page with the tabs does work.
<script>
$('.tab').click(function() {
$('.tab').removeClass("active");
$(this).addClass("active");
var panel = $(this).attr("data-panel")
$('.panel').hide();
$('#' + panel).show();
});
</script>
My question is this: First why is this happening? Second, is there a way to overcome this issue?
You are missing the . from $(.tab).click listener.
So I'm not that great with Javascript so I'll put that forward right away. That being said, I've looked up as much as I could on this particular problem before asking, but the suggestions haven't solved my issues. I'm ultimately trying to pull all of the links from an iframe window on the same domain as the main page. Then I want to basically search that link array to match it with the current page to trigger a CSS modification to the html code (this part is not coded yet, FYI). So here is the part I have so far: Side note: The confirms are in there to debug the code and try to tell me where it's failing and what my queries are returning, they won't stay obviously when this is finished. I appreciate any advice that may help me fix this!
<script type="text/javascript">
// main is the iframe that I'm trying to search for a tags
document.getElementById("main").onload = function() {
confirm("test");
var main = document.getElementById("main");
var anchors = main.contentWindow.document.getElementsByTagName('a');
confirm(anchors[1]);
for (var i in anchors) {
confirm(anchors[i].getAttribute("href"));
}
};
</script>
I have created a plunker for you its working. I think its the placement of code in your file is causing the problem.
<iframe id="main" src="content_if.html"></iframe>
<script>
// main is the iframe that I'm trying to search for a tags
document.getElementById("main").onload = function() {
confirm("test");
var main = document.getElementById("main");
var anchors = main.contentWindow.document.getElementsByTagName('a');
confirm(anchors[1]);
for (var i in anchors) {
confirm(anchors[i].getAttribute("href"));
}
};
</script>
You should use jQuery to do this in a cross browser way. Include jQuery in page
<script src="https://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
and follow this post
There is a similar post about doing this and I agree with Mohamed-Yousef. If you can use jquery then you should do so!
$("#main").contents().find("a").each(function(element) {
// "each" will iterate through every a tag and inject them as the "element" argument
// visible in the scope of this anonymous function
});
EDIT:
You must include
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
above your code that references the $ variable. There are other ways to use jQuery but this is probably the easiest.
I building an app in Rails 4. I have the below JS plugins on my show page but they seem to work only when I manually reload the page.
I read that this might be due to turbolinks but since these plugins are in the body and not the head, it doesn't seem like a turbolinks issue.
I even tried pasting these in the head and the footer tags but these gave me the same issue. Sometimes, one of the plugins loads and the other doesn't.
The first plugin below are AddThis social buttons and the other is an image gallery.
Any suggestions?
Demo page http://mktdemo.herokuapp.com/listings/45
<script type="text/javascript" src="//s7.addthis.com/js/300/addthis_widget.js#pubid=ashfaaq">
</script>
<script>
jQuery(document).ready(function($) {
$('#gallery-1').royalSlider({
fullscreen: {
enabled: true,
nativeFS: true
},
controlNavigation: 'none',
autoScaleSlider: true,
autoScaleSliderWidth: 960,
autoScaleSliderHeight: 850,
imageScaleMode: 'fit-if-smaller',
navigateByClick: true,
numImagesToPreload:2,
arrowsNav:true,
arrowsNavAutoHide: true,
/* arrowsNavHideOnTouch: true, */
keyboardNavEnabled: true,
fadeinLoadedSlide: true,
globalCaption: false,
globalCaptionInside: false,
loop: true,
thumbs: {
appendSpan: true,
firstMargin: true,
paddingBottom: 4
}
});
});
</script>
You can find the answer to your question here:
Rails 4 turbo-link prevents jQuery scripts from working
Rails 4 is using Turbolinks, which load the page javascript and css only once and then replaces only the body and title in the header. This is why jQuery(document).ready will only work if you reload the page.
In order to trigger your javascript, you have to use Turbolinks events like
page:change the page has been parsed and changed to the new version and on DOMContentLoaded
or
page:load is fired at the end of the loading process.
More details here: https://github.com/rails/turbolinks/#events
Here you can just replace your
jQuery(document).ready(function($) {
...
By
$(document).on('page:load', function($) {
...
Sounds like turbolinks is the cause of your problems.
You can either install this gem: Jquery Turbolinks
Or you could replace
jQuery(document).ready(function(
....
));
with
$(document).on('page:load', function(
....
));
Here is a nice copy/paste of why turbolinks causes some problems with jquery.
TurboLinks is a PJAX like library that asynchronously requests the content of the next page and inserts it into the current page’s body instead of requiring a full page reload. It speeds up page load times nicely. However, because the page is reloaded, the DOMContentLoaded event is triggered and your jQuery document.ready event wont be triggered.
Source
I had the same issue with my jQuery image gallery on Rails 5. It would not load unless the page was refreshed. After a year of (on and off) trying, here's the hack that worked for me:
Wrap your jQuery gallery code in a function:
var delayLoad = function(){
$('#gallery-1').royalSlider({
...
})
}
Then call it with setTimeout:
setTimeout(delayLoad, 250)
This solved the issue for me every time without having to refresh the page.
In my Rails View template, I'm using some jQuery for tabbed panels functionality:
<section>
... content ommitted
</section>
<script>
$(document).ready(function () {
$('.accordion-tabs-minimal').each(function(index) {
$(this).children('li').first().children('a').addClass('is-active').next().addClass('is-open').show();
});
$('.accordion-tabs-minimal').on('click', 'li > a', function(event) {
if (!$(this).hasClass('is-active')) {
event.preventDefault();
var accordionTabs = $(this).closest('.accordion-tabs-minimal')
accordionTabs.find('.is-open').removeClass('is-open').hide();
$(this).next().toggleClass('is-open').toggle();
accordionTabs.find('.is-active').removeClass('is-active');
$(this).addClass('is-active');
} else {
event.preventDefault();
}
});
});
</script>
Because I'm also using this script in other View templates, and I want to organize my Javascript a bit better, I created a Javascript file (tabbed_panels.js) under app/assets/javascripts and moved the above script to the tabbed_panels.js file.
However now the panels on my page have no content the first time the page is loaded. Only when the page is refreshed, the panels get loaded with content.
Does someone have an idea what's going on and how this can be solved, so my tabbed panels have content at the first page load?
thanks for your help,
Anthony
Turbolinks
The likely issue you have will be that you're trying to load the $(document).ready function with Turbolinks running.
This simply won't work (if you're using Turbolinks), as since Turbolinks refreshes only the <body> tag of your page, it will typically prevent your JS from binding to the various elements in the DOM, as the JS has not been reloaded
The way to fix this is to develop your JS around Turbolinks (using Turbolinks' event handlers):
#app/assets/javascripts/tabbed_panels.js
var new_items = function() {
$('.accordion-tabs-minimal').each(function(index) {
$(this).children('li').first().children('a').addClass('is-active').next().addClass('is-open').show();
});
$(document).on('click', '.accordion-tabs-minimal li > a', function(event) {
if (!$(this).hasClass('is-active')) {
event.preventDefault();
var accordionTabs = $(this).closest('.accordion-tabs-minimal')
accordionTabs.find('.is-open').removeClass('is-open').hide();
$(this).next().toggleClass('is-open').toggle();
accordionTabs.find('.is-active').removeClass('is-active');
$(this).addClass('is-active');
} else {
event.preventDefault();
}
});
});
$(document).on("page:load ready", new_items);
This should allow your tabs to be populated on page load, regardless of whether Turbolinks is operating or not.
--
Unobtrusive JS
Something else to consider (you've already done this), is that you really need to use unobtrusive javascript in your application.
Unobtrusive JS basically means that you're able to abstract your "bindings" from your page to your Javascript files in the asset pipeline. There are several important reasons for this:
Your JS can be loaded on any page you want (it's DRY)
Your JS will reside in the "backend" of your app (won't pollute views)
You'll be able to use the JS to populate the various elements / objects you want on screen
It's always recommended you put your JS into separate files - including in the views sets you up for a big mess down the line