How to use mailerlite popups in a Next.js application - javascript

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).

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();
})();
`,
}}
/>,
])
}

Is it possible to create a web component anyone can use without installation?

I'm just getting started with web components, and if I understand correctly, the point is for them to be reusable by anyone. Is it possible to create a component that can be used by anyone simply by adding a piece of html to their static site (similar to how JavaScript widgets are added, simply by copy-pasting a few lines of code), or does it need to be installed by someone? Or is this not an intended use case of web components?
Yes. A Web Component is a kind of "Javascript widget".
Typicially, you define a Web Component in a Javascript file.
You can then include it in any HTML with a <script> element.
Example with a minimal custom element called <hello-world>:
In hello-world.js:
customElements.define( 'hello-world', class extends HTMLElement {
constructor() {
super()
this.attachShadow( {mode: 'open' })
.innerHTML = 'Hello World!'
}
} )
In your main page index.html:
<html>
<head>
<!-- import the web component -->
<script src="hello-world.js">
</head>
<body>
<!-- use the new element -->
<hello-world></hello-world>
</body>
</html>
Note: alternately, one could also copy-paste the Javascript code that defines the custom element to its main HTML page.

Gatsby-js client side javascript inline in blog post

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>
);
}
}

Flashdevelop issue with publishing

I have created a simple flash object to redirect a browser to a different webpage using "navigatetoURL" while getting the URL from externalinterface calls to the javascript that the page doing the embedding of the flash file has.
I am able to build everything just fine and the html page I created (using swfobject.embedSWF to embed the flash file) runs fine and redirects the browser. However when I move the files that are needed for everything to function (the .swf file, swfobject.js, and the html file that embeds the flash object) the webpage does not redirect anymore. Just a blank space, which seems to be the flash object, is displayed and nothing is redirected.
Is there any compile option in Flashdevelop that I'm missing to prevent correct this?
Here is the actionscript 3 code:
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.net.navigateToURL;
import flash.net.URLRequest;
import flash.net.URLVariables;
import flash.external.ExternalInterface;
public class FlashTest extends Sprite
{
public function FlashTest()
{
var url:String = ExternalInterface.call("GetURL");
var hash:String = ExternalInterface.call("GetHash");
var new_url:String = url + hash;
var request:URLRequest = new URLRequest(new_url);
navigateToURL(request, "_self");
}
}
}
The HTML code:
<html>
<head>
<script src='js/swfobject.js' type='text/javascript'></script>
<script type='text/javascript'>
swfobject.embedSWF('Flashtest.swf', 'altContent', '100%', '100%', '10.0.0');
function GetURL()
{
return 'http://www.cnn.com';
}
function GetHash()
{
return '?hash=2398asb9s8234';
}
</script>
</head>
<body>
<div id='altContent'>
<h1>Flash_test</h1>
<p>Alternative content</p>
<p><a href='http://www.adobe.com/go/getflashplayer'><img
src='http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif'
alt='Get Adobe Flash player' /></a></p>
</div>
</body>
</html>
Actually, I've been in trouble with swf's being started before they are added to the HTML DOM and/or before the DOM is ready. Have a look at Adobes own article on ExternalInterface.call and javascript isReady.
I would also have a look at the allowScriptAccess parameter:
<script type="text/javascript">
var so = new SWFObject("movie.swf", "mymovie", "400", "200", "8", "#336699");
so.addVariable("allowScriptAccess", "always");
so.write("flashcontent");
</script>
It's actually a flash player security issue. If you move the files out of the bin folder and run them local they won't work any more.
Each time you create a project in flashdevelop the "bin" folder is added to the exception list of the flash player.
If you move the files to another folder then they won't work anymore hence the blank page when opening the html in browser.
The solution is to manually modify the flash player security config file and add the new path or to visit:
http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html
Click on Global Security Settings > Always Allow > Edit ocations > Add Location > Browse for the new path, where the files are located.

Categories

Resources