Gatsby-js client side javascript inline in blog post - javascript

I am trying to setup a Blog using Gatsby-JS. I have some posts in markdown that contain inline javascript.
As an example:
<script>window.alert("hello");</script>
I test the site using the command "Gatsby serve"
When I browse to my post via the index of the blog. The script is not executed. In the web console there are no errors.
When on the post page itself. If I do F5 or ctrl-f5 then the "hello" alert is displayed.
After uploading the site to github pages this behavior changes. I cannot get the script to execute by F5 or by navigating via the index. Only when I press ctrl+F5 the script is executed.
live test-blog can be found here (it show multiple alerts and tries to load plotly). https://dwjbosman.github.io/lstm-neural-network-for-sequence-learning/

Dinne's solution worked for me. Nice one! :)
I did need to avoid the use of JQuery though so I have posted here a version that doesn't rely on it:
componentDidMount() {
// Allow in-line JS scripts to be run
let scripts = document.querySelectorAll('[data-inline-script="data-inline-script"]');
scripts.forEach(function forEachScript(element) {
const script = element.innerHTML;
window.eval(script);
});
This will work with the following HTML:
<script data-inline-script="data-inline-script">
console.log("this works");
</script>
I am using this on a very basic static site. I'm not sure how much I like using eval() to be honest but it should cause no harm in my use-case.
Update
Although the above does work, I also need to include scripts using <script src="..."> which doesn't work :( This feels quite hacky too.

Checkout this question React: Script tag not working when inserted using dangerouslySetInnerHTML
Script in HTML that is inserted by React's dangerouslySetInnerHTML won't get executed. The linked question has a work around that perhaps you can use.

The previous answer pointed me in the right direction.
I gave all the inline scripts an attribute data-my-script. Next I added the following to layouts/index.jsx (not html.jsx as this is rendered only server side).
componentDidMount() {
let scripts = window.jQuery.find('[data-my-script]')
console.log(scripts);
scripts.forEach(function forEachScript(element) {
const script = window.jQuery(element).text();
window.eval(script);
});
}
render() {
const { children } = this.props;
return (
<Navigation config={config} LocalTitle={this.getLocalTitle()}>
<div ref={contentElement => (this.contentElement = contentElement)}>
<Helmet>
<meta name="description" content={config.siteDescription} />
// for plotly inside notebooks
// <script src="https://cdn.plot.ly/plotly-latest.min.js" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.min.js"/>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"/>
</Helmet>
{children()}
</div>
</Navigation>
);
}
}

Related

Snipcart and NextJS installation

I feel like this is driving me crazy, not being able to get Snipcart set up properly and I am not sure how to resolve this. Maybe someone can tell me what I'm missing here?
So I can add the link references in the 'Head' of the '_document' page just fine, but on Snipcart's installation docs, it says to put this script right after the element, except it throws errors anywhere I put it. Here is the installation script you need to add:
<script>
window.SnipcartSettings = {
publicApiKey: "YOUR_API_KEY",
loadStrategy: "on-user-interaction",
};
(()=>{var c,d;(d=(c=window.SnipcartSettings).version)!=null||(c.version="3.0");var s,S;(S=(s=window.SnipcartSettings).timeoutDuration)!=null||(s.timeoutDuration=2750);var l,p;(p=(l=window.SnipcartSettings).domain)!=null||(l.domain="cdn.snipcart.com");var w,u;(u=(w=window.SnipcartSettings).protocol)!=null||(w.protocol="https");var f=window.SnipcartSettings.version.includes("v3.0.0-ci")||window.SnipcartSettings.version!="3.0"&&window.SnipcartSettings.version.localeCompare("3.4.0",void 0,{numeric:!0,sensitivity:"base"})===-1,m=["focus","mouseover","touchmove","scroll","keydown"];window.LoadSnipcart=o;document.readyState==="loading"?document.addEventListener("DOMContentLoaded",r):r();function r(){window.SnipcartSettings.loadStrategy?window.SnipcartSettings.loadStrategy==="on-user-interaction"&&(m.forEach(t=>document.addEventListener(t,o)),setTimeout(o,window.SnipcartSettings.timeoutDuration)):o()}var a=!1;function o(){if(a)return;a=!0;let t=document.getElementsByTagName("head")[0],e=document.querySelector("#snipcart"),i=document.querySelector(`src[src^="${window.SnipcartSettings.protocol}://${window.SnipcartSettings.domain}"][src$="snipcart.js"]`),n=document.querySelector(`link[href^="${window.SnipcartSettings.protocol}://${window.SnipcartSettings.domain}"][href$="snipcart.css"]`);e||(e=document.createElement("div"),e.id="snipcart",e.setAttribute("hidden","true"),document.body.appendChild(e)),v(e),i||(i=document.createElement("script"),i.src=`${window.SnipcartSettings.protocol}://${window.SnipcartSettings.domain}/themes/v${window.SnipcartSettings.version}/default/snipcart.js`,i.async=!0,t.appendChild(i)),n||(n=document.createElement("link"),n.rel="stylesheet",n.type="text/css",n.href=`${window.SnipcartSettings.protocol}://${window.SnipcartSettings.domain}/themes/v${window.SnipcartSettings.version}/default/snipcart.css`,t.prepend(n)),m.forEach(g=>document.removeEventListener(g,o))}function v(t){!f||(t.dataset.apiKey=window.SnipcartSettings.publicApiKey,window.SnipcartSettings.addProductBehavior&&(t.dataset.configAddProductBehavior=window.SnipcartSettings.addProductBehavior),window.SnipcartSettings.modalStyle&&(t.dataset.configModalStyle=window.SnipcartSettings.modalStyle),window.SnipcartSettings.currency&&(t.dataset.currency=window.SnipcartSettings.currency),window.SnipcartSettings.templatesUrl&&(t.dataset.templatesUrl=window.SnipcartSettings.templatesUrl))}})();
</script>
Putting this script anywhere in '_document' throws an error, after doing a bunch of trial and error. I cannot figure out how to get Snipcart working properly with NextJS and documentation on installation alone seems to be hard to find - which means people don't usually have an issue with it, right? Haha. I am losing my mind on this, and it's probably an easy fix I have overlooked somewhere. Any help is appreciated!
This is where the script is located, in '_document':
import { Html, Head, Main, NextScript } from "next/document";
export default function Document() {
return (
<Html>
<Head>
...
</Head>
<body>
<Main />
<NextScript />
<script>
window.SnipcartSettings = {
publicApiKey: "YOUR_API_KEY",
loadStrategy: "on-user-interaction",
};
(()=>{var c,d;(d=(c=window.SnipcartSettings)...
</script>
</body>
</Html>
);
}
Found the solution here:
NextJS Snipcart Installation - github
Omit the 3 links. Not needed.

Gatsby site, Script not available on first page request with Helmet

I have a Gatsby site that I'm trying to add a third-party js widget to but for some strange reason it will only load if I refresh the page manually. On first page load, it as is if it doesn't exist at all, no errors in dev-tools, nothing... but if I refresh the page, it then appears. It's almost as if it's lazy loading? Is there a way to force load?
I've checked the elements, console, and network tabs in dev tools but there's nothing to indicate any errors. elements shows the tags that I would expect, console shows nothing at all, and network everything shows up with 200.
Could this be an issue with Gatsby and/or Helmet, it might be but I don't think it's an issue with the widget itself (it's third-party, I have no control over it, see last paragraph)?
<Helmet
script={[
{
type: 'text/javascript',
src: '//widget-url.com/path/to/jsfile.min.js',
},
{
type: 'text/javascript',
innerHTML: `
(function() {
var widget = Stuff.happens.here();
widget.Initialise();
})();
`,
},
]}
/>
In the body I then have:
<div id='widget-id'></div>
Things I've tried to attempt to understand where the issue is:
As I mentioned, I have to force refresh the page where the widget is located. If I force refresh any other page, it doesn't help. So something I tried is: rather than only including the JS into the head of the page in question, I would including it on ALL pages. But this has made no difference.
I've also tried adding the widget to a simple stand-alone html file, the widget loads without problem. Which leads me to think that it's probably not a widget issue?
I don't know where to go from here :(
The problem is that you are pointing a DOM element that may or may not be rendered at the moment your request the script.
In your case, I'd try:
<Helmet>
<script async defer src="//widget-url.com/path/to/jsfile.min.js" />
<script async defer>
{`
(function() {
var widget = Stuff.happens.here();
widget.Initialise();
})();
`}
</script>
</Helmet>
Or using one of the multiple Server-Side Rendering APIs. onRenderBody should work:
// gatsby-ssr.js
import React from "react"
export const onRenderBody = ({ setHeadComponents, setPostBodyComponents }) => {
setHeadComponents([
<script
src="//widget-url.com/path/to/jsfile.min.js"
type="text/javascript"
async
/>,
<script
dangerouslySetInnerHTML={{
__html: `
(function() {
var widget = Stuff.happens.here();
widget.Initialise();
})();
`,
}}
/>,
])
}

How to use mailerlite popups in a Next.js application

I'm integrating a mailerlite popup for a client's next.js project, and I'm having a difficult time converting the JavaScript snippets into the jsx required to make the popups function properly. On first load it seems to work just fine, but on relaod I'm getting the following error.
window is not defined
I've encountered the issue while dealing with DOM manipulation, but in this case, judging from the code in the snippet, I need the window object.
Install the following snippet of Javascript on every page of your website right before the closing tag.You only need to add this snippet once, even if you plan to have a few different webforms.
<!-- MailerLite Universal -->
<script>
(function(m,a,i,l,e,r){ m['MailerLiteObject']=e;function f(){
var c={ a:arguments,q:[]};var r=this.push(c);return "number"!=typeof r?r:f.bind(c.q);}
f.q=f.q||[];m[e]=m[e]||f.bind(f.q);m[e].q=m[e].q||f.q;r=a.createElement(i);
var _=a.getElementsByTagName(i)[0];r.async=1;r.src=l+'?v'+(~~(new Date().getTime()/1000000));
_.parentNode.insertBefore(r,_);})(window, document, 'script', 'https://static.mailerlite.com/js/universal.js', 'ml');
var ml_account = ml('accounts', '912433', 'd5p1f7l9g0', 'load');
</script>
<!-- End MailerLite Universal -->
I've placed this code in my Layout wrapper. As previously stated, it works fine on first load, but as soon as the user navigates to a new page above error shows up.
PS I found an old question regarding this topic here, but it's old and not quite relevant to my situation. I need to figure out how to convert the above snippet for nextjs. Any help at all would be appreciated.
This approach treats the MailerLite universal tag as its own <script> hosted on your site's domain.
Add a NextJS custom document.
Create a JavaScript file containing the MailerLite universal tag code in ./public. I put mine in ./public/scripts/ml.js.
Add a <script> tag loading #2 in your custom _document.js file:
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html>
<Head>
<script async src="/scripts/ml.js"></script>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
Everything worked as intended from there! (Caveat: I'm only using embedded forms).

jQuery's include method doesn't work

As my website has only one page, and the index.html was getting really long and impossible to read. So I decided to put each section in a different HTML file and use jQuery to included it.
I used jQuery's include in the way as it has been mentioned here to include a external HTML file but apparently it doesn't work for my website. I really don't know what is the problem.
Here is the link of my workspace.
Here is what I am doing in index.html file to include other sections
<script src="./js/jquery-1.11.1.min"></script>
<script>
$(function() {
$("#includedContent").load("./page1.html");
});
</script>
<script>
$(function() {
$("#includedContent").load("./page2.html");
});
</script>
<script>
$(function() {
$("#includedContent").load("./page3.html");
});
</script>
<script>
$(function() {
$("#includedContent").load("./page4.html");
});
</script>
<script>
$(function() {
$("#includedContent").load("./page5.html");
});
</script>
<script>
$(function() {
$("#includedContent").load("./page6.html");
});
</script>
<script>
$(function() {
$("#includedContent").load("./page7.html");
});
</script>
I also used this method to make sure the file is accessible and everything was fine. So the problem is not the accessibility of the files
You are overwriting the contents of #includedContent seven times (see documentation of jQuery.load). With AJAX, there is no guarantee which request will complete first so you will end up with random page content inside the container.
The solution is to create containers for each page and load each page inside its dedicated container, something like this:
<div id="includedContent">
<div class="page1"></div>
<div class="page2"></div>
<div class="page3"></div>
</div>
$(document).ready(function() {
$("#includedContent .page1").load("page1.html");
$("#includedContent .page2").load("page2.html");
$("#includedContent .page3").load("page3.html");
});
NB: Having said all that, I do not understand how AJAX solves the problem of the page being too long/impossible to read.
There are several things that look odd to me:
all your load functions run at document ready, which is weird while having all the same target. load replaces (not adds) the content of the selected element with what is being loaded, you probably are trying to add all the html contents, but your current setup would actually just load page7.html into #includedContent
the paths look strange to me, i guess ./ may cause errors, try to leave out ./ everywhere.
rather than loading an entire html page, you might just want to load a piece of that file (i dont know how pageX.html looks), for example you would not want to load the <html> node entirely, rather the content only: .load('page1.html #content')
are you including jquery correctly? there is no .js in your inclusion

Meteor.js: <script> tag doesn't work inside <body>

A simple script tag inside the body tag doesn't seem to work. The alert doesn't get triggered in the code below:
<body>
<script type="text/javascript">
alert('Hello');
</script>
{{>main}}
</body>
Any idea why?
Edit:
Just tried it with a fresh meteor app, no alert tag still:
<head>
<title>test</title>
</head>
<body>
<script type="text/javascript">
alert('Hello');
</script>
{{> hello}}
</body>
<template name="hello">
<h1>Hello World!</h1>
{{greeting}}
<input type="button" value="Click" />
</template>
Weird thing is when I copy paste the source of the html, made a new html page, and the alert will work.
Edit3: I deployed this app here: http://alert-in-body-test.meteor.com/
Do you get an alert box?
This question is still relevant in the current version of Meteor (version 0.5.4) so I wanted to describe how to include script at the end of the body.
To execute javascript at the end of the body, register a Handlebars helper and put the relevant code there, like this:
In client.html:
<body>
{{renderPage}}
{{afterBody}}
</body>
...
In client.js:
if (typeof Handlebars !== 'undefined') {
Handlebars.registerHelper('afterBody', function(name, options) {
$('body').append('AFTER BODY');
});
}
(For a great description of why this is required, see Rahul's answer to a similar question here: https://stackoverflow.com/a/14002991/219238 )
Its working for me
in onrender call this jquery
$.getScript('yours url')
It should work.
I have just pasted this inside one of my projects and it worked.
Your {{>main}} is strange for me tough. Also make sure that <body> is inside <html> tag.
Meteor is constructing the entire DOM from Javascript by rendering your page as a template -- the 'source' for your page as seen by the browser is basically:
<script type="text/javascript" src="/5a8a37bef5095c69bcd3844caf3532e1ba6d49bf.js"></script>
I can't find a definitive page stating that embedding a script tag in a template like this won't cause it to be executed, but it definitely feels against the spirit of what the framework is trying to achieve.
If the point is to achieve a clean separation of your markup and logic then why put it in the template. A better solution would be to use the meteor.startup call
Instead of looking the rendered html at developer tools, try looking the real downloaded html.
You will find a (probably) empty tag, with tons of script tags inside the .
In other words, the body of your meteor application is not the body of the final html, it's just your main template.
Instead, this ton on scripts shipped by Meteor, will load your templates.
So, your code will not run, cause it's been placed there. It's like when you manipulate DOM (with jQuery, for exemple), placing a script tag in DOM, after it's loaded. This script tag will not run.

Categories

Resources