-
-
Notifications
You must be signed in to change notification settings - Fork 876
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Include codeinjection for site and posts #22
Comments
Any news on this ? |
refs TryGhost/gatsby-starter-ghost#22 - There is no way in Gatsby of injecting code at the end of the `<body>` tag, which means, everything has to go into the `<head>` - For this reason, we concat all codeinjections for each, posts, pages, and settings (site) - In Gatsby, we have to use `react-helmet` to inject script and style tags. But those have to be declared separately. - Because the upstream dependency to parse and select the `<style>` and `<script>` tags doesn't work properly with the `<script>` tags, I only added `<style>` for now. - Adding a new node field `codeinjection_styles` to posts, pages, and settings, which includes all concatenated style tags from head and foot code injection.
refs #22 - Update [email protected] which includes the new node fiels `codeinjection_styles` - Inject styles with react-helmet into - Layout for site codeinjection - Post template for post codeinjection - Page template for page codeinjection
Code injection is now working for The code for that parses the content is here. If anyone has an idea, how to solve this, please let me know, or - even better - submit a PR 😊 |
@AileenCGN Personally, I have implemented this in my own Fork of this repository. What I have found, as a workaround, is to place the functions themselves inside of the Ghost CMS without |
Thank you @ZionDials for this! This might be a good solution for some users already. The problem is, that we have to cover the case that both, |
I'm wondering how this issue became stale as it seems to be a major feature and therefore I expected this to be needed by many ... but anyhow, I just came to be in need of this too so I implemented it in a way that I think could serve as a possible solution, even while it's still a little bit hacky some might say but actually works like a charm. First, we build ourselves some kind of function that can extract our wanted script tags from the data delivered by const extractCodeInjectionElements = (
type: "script" | "style",
scriptString: string
) => {
let scriptElements = []
if (documentGlobal) {
// We create a script wrapper to render the scriptString to real HTML
const scriptWrapper = documentGlobal.createElement("div")
scriptWrapper.innerHTML = scriptString
// Now we can use native HTML queries to get our script|style elements (in this case a
// collection)
const scriptsCollection = scriptWrapper.getElementsByTagName(type)
// Lets map the collection to a "normal" array of script elements
for (let i = 0; i < scriptsCollection.length; i++) {
scriptElements.push(scriptsCollection.item(i))
}
}
// And return them ...
return scriptElements
} Note that I created that function to work with 2 types of tags, // Transform the codeinjection string to an array of "real" HTML script elements
const headScriptElements = extractCodeInjectionElements(
"script",
data.ghostPage?.codeinjection_head || ""
)
const footScriptElements = extractCodeInjectionElements(
"script",
data.ghostPage?.codeinjection_foot || ""
)
// As `codeinjection_styles` prop is always undefined, we just extract the style elements
// from head and foot on our own.
const styleElements = extractCodeInjectionElements(
"style",
"".concat(
data.ghostPage?.codeinjection_head || "",
data.ghostPage?.codeinjection_foot || ""
)
) So that's basically it. Now we just map over the returned elements in our JSX, create the respecting script or style tags and "fill" them with the raw code by accessing the import { graphql } from "gatsby"
import React from "react"
import { Helmet } from "react-helmet"
import Layout from "../components/Layout"
import { documentGlobal } from "../utils/helpers"
const Page: React.FC<{ data: GatsbyTypes.GhostPageQuery }> = ({ data }) => {
const extractCodeInjectionElements = (
type: "script" | "style",
scriptString: string
) => {
let scriptElements = []
if (documentGlobal) {
// We create a script wrapper to render the scriptString to real HTML
const scriptWrapper = documentGlobal.createElement("div")
scriptWrapper.innerHTML = scriptString
// Now we can use native HTML queries to get our script elements (in this case a
// collection)
const scriptsCollection = scriptWrapper.getElementsByTagName(type)
// Lets map the collection to a "normal" array of script elements
for (let i = 0; i < scriptsCollection.length; i++) {
scriptElements.push(scriptsCollection.item(i))
}
}
// And return them ...
return scriptElements
}
// Transform the codeinjection string to an array of "real" HTML script elements
const headScriptElements = extractCodeInjectionElements(
"script",
data.ghostPage?.codeinjection_head || ""
)
const footScriptElements = extractCodeInjectionElements(
"script",
data.ghostPage?.codeinjection_foot || ""
)
// Same for the style elements
const styleElements = extractCodeInjectionElements(
"style",
"".concat(
data.ghostPage?.codeinjection_head || "",
data.ghostPage?.codeinjection_foot || ""
)
)
return (
<Layout>
<Helmet>
{/* Now we just iterate over the elements and create a script tag for each
of them inside Helmet to exactly replicate our script tags from Ghost
(ghost_head in this case). */}
{headScriptElements.map((script, index) => (
<script
key={`ci_h_${index}`}
type="text/javascript"
src={script?.getAttribute("src") || undefined}
async={script?.hasAttribute("async")}
>{`${script?.innerText}`}</script>
))}
{/* Or, if we want it clean, we just join multiple elements into one. */}
<style type="text/css">{`${styleElements
.map((e) => e?.innerText)
.join("")}`}</style>
</Helmet>
{/* Our normal html from Ghost. */}
<div className="tw-px-6 sm:tw-px-8 lg:tw-px-10 tw-py-8 tw-content-page">
<div
dangerouslySetInnerHTML={{ __html: data.ghostPage?.html || "" }}
></div>
</div>
{/* Finally, add the scripts from the ghost_foot section */}
{footScriptElements.map((script, index) => (
<script
key={`ci_f_${index}`}
type="text/javascript"
src={script?.getAttribute("src") || undefined}
async={script?.hasAttribute("async")}
>{`${script?.innerText}`}</script>
))}
</Layout>
)
}
export default Page
// This page query loads all posts sorted descending by published date
// The `limit` and `skip` values are used for pagination
export const pageQuery = graphql`
query GhostPage($slug: String!) {
ghostPage(slug: { eq: $slug }) {
...GhostPageFields
}
}
` So there you go, that's my implementation. If you guys like it you could basically implement an improved version of the EDIT: forgot to add the src and async attributes to the final |
refs TryGhost#22 - Update [email protected] which includes the new node fiels `codeinjection_styles` - Inject styles with react-helmet into - Layout for site codeinjection - Post template for post codeinjection - Page template for page codeinjection
refs TryGhost#22 - Update [email protected] which includes the new node fiels `codeinjection_styles` - Inject styles with react-helmet into - Layout for site codeinjection - Post template for post codeinjection - Page template for page codeinjection
Problem description
codeinjection_head
andcodeinjection_foot
for the site as well as for posts is currently not used in the starter. The reason for that isa) it's a bit tricky with scripts in React (hence our dirty little script here
b) code injection can contain almost anything, but most likely
<script>
or<style>
tags, which we need to differentiate for reasons that I'll describe further downhtml.js
but would need to have the Ghost settings available at that point already (needs Global site/config values #20)html.js
, as we need the codeinjection per post and not global and we’d prefer to add it to the<head>
section, like we do in Ghost frontend → https://www.gatsbyjs.org/docs/custom-html/#react-helmethtml.js
requires that we know already if it’s a<script>
,<style>
, or any other tag we can think of, as it needs to be specified → https://github.com/nfl/react-helmet#reference-guidedangerouslySetInnerHTML
) of a post doesn’t work either, as it needs to be wrapped in a parent element, which then prevents any of the injected tags from being executed. Furthermore, we would have it in our<body>
instead of the<head>
Proposal
Differentiate the
codeinjection
contentOne possible solution is to create a parser util that differentiates between
<script>
and<style>
tags and returns e. g. an Object with those values like this:☝️ This is just one idea and open for discussions/suggestions!
Site codeinjection (
settings.codeinjection_head
&settings.codeinjection_foot
) inhtml.js
The site codeinjection can be injected into the
html.js
file. 👉 https://www.gatsbyjs.org/docs/custom-html/ but there is - of course - a tricky part here too: thehtml.js
generation happens before the schema generation and doesn't allow GraphQL queries. This relies therefore on the outcome of #20 is implemented, which is supposed to make those Ghost settings available on pre build time.Post codeinjection (
post.codeinjection_head
&post.codeinjection_foot
) inpost.js
andpage.js
The codeinjection for the
<head>
can be solved using React Helmet like described here.The
codeinjection_foot
is not clear where to add it, as the recommended way to inject scripts and styles is using React Helmet. If there's no better way possible to insert thecodeinjection_foot
before the closing</body>
tag, it's probably best to use the same implementation as for the head.Todos
codeinjection_foot
codeinjection_scripts
The text was updated successfully, but these errors were encountered: