I am building a website using pure HTML/Javascript. I created an HTML component for the header and footer, and want the component to call a small script that changes the style of the navigation link when on that correlating page. Here is my full component, header.js (I've simplified the HTML):
const headerTemplate = document.createElement('template');
headerTemplate.innerHTML = `
<section id="header" class="fade-in">
<div class="header-hero">
<h1 class="cursor">Integrated Mind-Body</h1>
<nav class="header-nav">
<ul>
<li class="cursor"><a id='index' href="index.html">Home</a></li>
<li class="cursor"><a id='about' href="about.html">About</a></li>
<li class="cursor"><a id='services' href="services.html">Services</a></li>
<li class="cursor"><a id='contact' href="contact.html">Contact</a></li>
</ul>
</nav>
</div>
</section>
`
class Header extends HTMLElement {
constructor() {super();}
connectedCallback() {
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.appendChild(headerTemplate.content);
}
}
customElements.define('header-component', Header);
The header-component is appearing fine in my code, but I can't get the component to interact with a very simple JS snippet:
var path = window.location.pathname;
var page = path.split("/").pop();
if (page === "index.html"){document.getElementById('index').classList.add('active')
I've tried adding a const script tag to the connectedCallback() and get errors loading index.js (where I have the snippet)
I've also tried putting a script tag within the innerHTML that has the function. I'm unable to find many resources for non-React components. This site is static and using React would be overkill. Any help is greatly appreciated!
Related
Basically I'm trying to remake some simple web page that I have initially created with HTML and CSS to be working rather on React. I managed to redo the page to correctly display when it was moved into React, however I don't really understand why the navigation links that I have on top do not take me to the corresponding section on the same page anymore as well as why the external links to the project sites also stopped working.
Here is the project link code:
import React from "react";
export default function ProjectTile(props) {
return (
<div className="project-tile" id={props.id}>
<a href={props.href} target="_blank" id={props.link_id}>
<img
className="project_screenshot"
src={props.img_src}
alt={props.img_alt}
/>
<p className="project_name">
<span className="brackets"><</span> {props.title}{" "}
<span className="brackets">/></span>
</p>
</a>
</div>
);
}
All props are getting mapped and loaded from the array with corresponding data where each object looks like this:
{
id: "tribute_page",
link_id: "https://codepen.io/konstantinkrumin/full/PooYQbG",
img_src: "https://i.imgur.com/ynRuzOQ.png",
img_alt: "tribute_page_screenshot",
title: "Tribute Page"
}
The navigation links used are the following:
import React from "react";
export default function Navbar() {
return (
<nav id="navbar">
<a className="nav-link" href="#welcome-section">
About
</a>
<a className="nav-link" href="#projects">
Projects
</a>
<a className="nav-link" href="#contact">
Contact
</a>
</nav>
);
}
And each section they refer to have an id corresponding to the href indicated above.
Here if the link to this project on codesandbox
P.S. Everything used to work correctly when it was on HTML.
Also the contact links that seem to be set in similar way as project links are working.
Here are two things I think I found out:
In the ProjectTile.js file, replace href = {props.href} by href={props.link_id and now project opens in codepen.
About the jump link you have made in nav-bar, I think it's because of problem of codesandbox.
If you manage to make your url to https://op6gq.csb.app#projects instead of https://op6gq.csb.app/#projects. That's gonna work.
Or directly visiting https://op6gq.csb.app/#welcome-section jump link too works well.
It looks like there's no href prop. Sounds like what you want is something like
href={`#${props.id}`}
which would evaluate to href="#tribute_page" in this example.
You Have to try that your page url become:
https://op6gq.csb.app#welcome-section
Not:
https://op6gq.csb.app/#welcome-section
please attend to that / in address bar!
We run a few ASP.NET Core applications that do come with some pages. These are all "classic" multipage applications.
For our new portal we've decided to go with piral. While we add a couple of new modules we also want to use the existing applications.
How can we integrate a multipage application in piral or in a clientside (SPA) micro-frontend?
One example for a multipage app looks like (hosted on some address like https://myexample.com/home)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Privacy Policy - DotnetApp</title>
<link rel="stylesheet" href="/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="/css/site.css" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" href="/">DotnetApp</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" href="/Home/Privacy">Privacy</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
<h1>Privacy Policy</h1>
<p>Use this page to detail your site's privacy policy.</p>
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2020 - DotnetApp - Privacy
</div>
</footer>
<script src="/lib/jquery/dist/jquery.min.js"></script>
<script src="/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="/js/site.js?v=4q1jwFhaPaZgr8WAUSrux6hAuh0XDg9kPS3xIVq36I0"></script>
</body>
</html>
We don't need to have all scripts - most of them are just there due to the boilerplate. Actually we can also run without all of them.
Migration of an MPA to Piral can be done in multiple ways.
Create a wrapper pilet that exposes the different SSRed parts via iframes
Create a wrapper pilet that exposes the different SSRed parts via fragments which are dynamically obtained
Convert your Razor views into React parts and have a split between FE/BE; put the React parts in a pilet
I don't know how complicated you application is, but maybe (1) would be the easiest to get started (especially if its, e.g., just a single page).
Let's see how (2) may be implemented:
import * as React from 'react';
export function setup(app) {
const connect = app.createConnector(() => fetch('https://myexample.com/home').then(res => res.text()));
app.registerPage('/myexample', connect(({ data }) => {
const __html = data;
return <div dangerouslySetInnerHTML={{ __html }} />;
}));
}
Here, we lazy load content from https://myexample.com/home and display it in the component (which will be a page).
There are a couple of things you should watch out for:
In the best case detect the fetch (or send a special header) and only return a fragment instead of the full response
If stylesheets are needed either "fish" them out of the response or integrate them upfront (see next point)
The code above has no special SPA link handling. Thus all links are relative to the given page URL and they are making a full transition ...
JavaScript would not be loaded / working in the given solution
Now regarding a stylesheet you could do (among other things):
import './my-style.css';
import * as React from 'react';
export function setup(app) {
// ...
}
where my-style.css looks like:
#import url(https://myexample.com/css/site.css);
We could have more URLs etc. here, but I leave that out on purpose. Keep in mind that the previously shown way could introduce collisions. So either "download" the sheet upfront and prefix ("scope") it, or use it all in a shadow DOM solution (shadow DOM is always "free" of the parent DOM's style and needs to import its own stylesheets).
For the link handling what you can do: Use a layout effect, get all a elements via a selector, attach an event and cancel the original navigation. Use the history context instead.
In code this looks similar to:
import * as React from 'react';
import { useHistory } from 'react-router';
export function setup(app) {
const connect = app.createConnector(() => fetch('https://myexample.com/home').then(res => res.text()));
app.registerPage('/myexample', connect(({ data }) => {
const __html = data;
const container = React.useRef();
const history = useHistory();
React.useLayoutEffect(() => {
if (container.current) {
const anchors = container.current.querySelectorAll('a[href]');
Array.prototype.forEach.call(anchors, anchor => {
anchor.addEventListener('click', ev => {
ev.preventDefault();
history.push(anchor.getAttribute('href'));
});
});
}
}, []);
return <div dangerouslySetInnerHTML={{ __html }} ref={container} />;
}));
}
I am trying to add a Tripadvisor rating widget to my React website (see code below). At the moment, only the Tripadvisor log is loading, and no rating information. I think the script in the widget is not running, and I'm not sure how to get it to run.
I loaded the script using Helmet, and can see it in the head of my document in the browser. In the same component, I try to render the widget.
//Here is the code of the widget:
<div id="TA_cdsratingsonlynarrow350" class="TA_cdsratingsonlynarrow">
<ul id="80bNGet6b" class="TA_links mE4BAE">
<li id="u9nO7YH4pF" class="aWtKBjB9S">
<a target="_blank" href="https://www.tripadvisor.com/">
<img
src="https://www.tripadvisor.com/img/cdsi/img2/branding/tripadvisor_logo_transp_340x80-18034-2.png"
alt="TripAdvisor"
/>
</a>
</li>
</ul>
</div>
<script
async
src="https://www.jscache.com/wejs
wtype=cdsratingsonlynarrow&uniq=350&locationId=17821239&lang=en_US&border=true&display_version=2"
data-loadtrk
onload="this.loadtrk=true">
</script>
//Here is the code of my attempt:
const Home = () => {
return (
<Layout>
<Helmet>
<script
async
src="https://www.jscache.com/wejs?wtype=cdsratingsonlynarrow&uniq=350&locationId=17821239&lang=en_US&border=true&display_version=2"
data-loadtrk
onload="this.loadtrk=true"
></script>
</Helmet>
<div id="TA_cdsratingsonlynarrow350" class="TA_cdsratingsonlynarrow">
<ul id="80bNGet6b" class="TA_links mE4BAE">
<li id="u9nO7YH4pF" class="aWtKBjB9S">
<a target="_blank" href="https://www.tripadvisor.com/">
<img
src="https://www.tripadvisor.com/img/cdsi/img2/branding/tripadvisor_log o_transp_340x80-18034-2.png"
alt="TripAdvisor"
/>
</a>
</li>
</ul>
</div>
</Layout>
I expect the full tripadvisor widget to load, but instead it just loads the Tripadvisor logo image (which does not require the script to be run).
The widget looks fine when I test it in a plain html document, but I can't get it to work in React. Any help much appreciated!
Thanks for the question. Receiving errors in the console (by right clicking and choosing inspect in Chrome) would be helpful to determine the root of the problem.
I think what's happening here is a conflict associated with the security of the site.
In the line that identifies the jscashe.com URL, use this URL instead: https://www.tripadvisor.com/WidgetEmbed-cdsratingsonlynarrow?amp;locationId=17821239&border=true&uniq=350&lang=en_US&display_version=2
It turns out that by doing a gatsby build && gatsby serve the widget works fine. I was previously running gatsby develop and it wasn't working then. If anyone knows why that could be I would be interested to know!
I'm new to react and I'm trying to render one component in different html files (because I'm working in an existing project), each of them with different text.
I'm thinking of something like this:
class ctaSection extends React.Component{
render(){
return(
<div className="cta-section">
<div className="md:w-9c">
<h5 className="uppercase">{this.props.h5}</h5>
<h3>{this.props.h3}</h3>
</div>
<div className="cta-button">
<a href="#">
<button className="w-full">{this.props.button}</button>
</a>
</div>
</div>
);
}
}
export default ctaSection;
Then, in my index.js, I'm rendering like this, passing the props:
let ctaPage1 = document.getElementById('cta-section-page-1');
let ctaPage2 = document.getElementById('cta-section-page-2');
ReactDOM.render(<CtaSection h3='my text for page 1' h5='my h5 for page 1' button='hello'/>, ctaPage1);
ReactDOM.render(<CtaSection h3='text for page 2' h5='something' button='click me'/>, ctaPage2);
I'm not sure if this is the best and simpler way to do this, because I'm calling ReactDOM.render twice for same component, and I got this error:
Uncaught Invariant Violation: Target container is not a DOM element.
This works fine if I render the component once, but not for multiple instances.
What is the best way to do it?
I've thought about this more, and I'm not experienced with adding React to existing websites, but here's what I'd suggest:
I would have 1 JS file per HTML page that imports CtaSection, each of those files would look like such:
// page1.js
import CtaSection from '../CtaSection'; // or wherever it is
let ctaPage = document.getElementById('cta-section-page');
ReactDOM.render(<CtaSection h3='my text for page 1' h5='my h5 for page 1' button='hello'/>, ctaPage);
You would just need to make sure each html page uses the corresponding JS file.
// page1.html
<body>
<div id="cta-section-page"></div>
<script src="page1.js"></script>
</body>
Repeat for each page.
I have a problem with inconsistent behavior between the site generated by gatsby develop and gatsby build. The result is a site that works in development but not production.
A summary of my site:
A simple blog-like site (personal profiles instead of blog posts). The index
page is a list of people, and each item in that list links to that person's profile page.
I'm using Gatsby to build the site. My data (the personal profiles) are entries hosted on the Contentful headless CMS. I'm using the gatsby-source-contentful source plugin.
High level description of the problem
I cannot shuffle the order of the profile list items on the index page. The only behavior where my site goes beyond any basic gatsby tutorial is that I want to randomize the list of profiles on my index page (to give everyone a fair chance at being listed at the top).
gatsby build generates a static index page with the list in one permutation.
When loaded in a browser the ThumbList component re-shuffles those items to another permutation on render and some sub-elements are not properly managed by react and stay stuck as other elements shift. This leads, for example, to profile images paired with the wrong name.
The code
The following code is somewhat summarized for readability.
src/pages/index.js:
import React from "react"
import Layout from "../components/layout"
import ThumbList from "../components/thumbList"
import { graphql } from "gatsby"
export default ({data}) => {
// people are called "creators" in the app
const creatorData = data.allContentfulCreator.edges
const shuffledData = shuffle(creatorData.slice(0))
return (
<Layout>
<ThumbList data={shuffledData} />
</Layout>
)
}
const shuffle = (a) => {
// Fisher-Yates randomized array in-place shuffle algo
// ...
return a
}
export const query = graphql`
{
allContentfulCreator {
edges {
node {
id
slug
name
bio {
id
bio
}
mainImage {
file {
url
}
}
}
}
}
}
`
src/components/thumbList.js:
import React from "react"
import { Link } from "gatsby"
// A list of creator profile links, with name and picture thumbnail
export default ({data}) => {
return (
<div>
<ul>
{
data.map(({node}) => {
const creator = node
const link = "/" + creator.slug
const image = "https:" + creator.mainImage.file.url
return (
<li key={creator.id}>
<Link to={link}>
{creator.name}
</Link>
<img src={image} />
</li>
)
})
}
</ul>
</div>
)
}
The result of gatsby build is an index.html containing:
<ul>
<li>
<a href="/alice">
Alice
</a>
<img src="cdn.com/alice.jpg">
</li>
<li>
<a href="/bob">
Bob
</a>
<img src="cdn.com/bob.jpg">
</li>
<li>
<a href="/eve">
Eve
</a>
<img src="cdn.com/eve.jpg">
</li>
</ul>
However, when viewing the index page in the browser (via gatsby serve or a deployed version of the site) the live react ThumbList component again shuffles the data in its render method.
The result re-rendered html:
<ul>
<li>
<a href="/alice">
Bob
</a>
<img src="cdn.com/alice.jpg">
</li>
<li>
<a href="/bob">
Eve
</a>
<img src="cdn.com/bob.jpg">
</li>
<li>
<a href="/eve">
Alice
</a>
<img src="cdn.com/eve.jpg">
</li>
</ul>
Here only the text nodes are rearranged to match the new order (confirmed by console logging the array order), but the links and image elements remain stuck where they were in the static build. Now the names, images, and links are scrambled.
Two other things to note:
All works fine with gatsby develop. I guess it's because in development index.html is generated without its static content in the body - allowing react complete control over the DOM from the start with no
static scaffolding to confuse it.
Using the react inspector I see that the virtual DOM and the real DOM have gotten out of sync. React thinks it has correctly shuffled the list items. Inspector shows something like:
(very abbreviated for readability)
<ul>
<li key="165e2405">
<GatsbyLink to="/bob">
Bob
</GatsbyLink>
<img src="cdn.com/bob.jpg"></img>
</li>
<li key="067f9afc">
<GatsbyLink to="/eve">
Eve
</GatsbyLink>
<img src="cdn.com/eve.jpg"></img>
</li>
<li key="ca4b82bf">
<GatsbyLink to="/alice">
Alice
</GatsbyLink>
<img src="cdn.com/alice.jpg"></img>
</li>
</ul>
My questions
Is this just an un-Gatsby-like approach? This description of a "Hybrid app page" seems to imply that you can either have static or dynamic components. I suppose I'm trying to have it both ways: I want the
profiles fetched from contentful via graphql during build so it can be available via static HTML + pre-built json data files (e.g. /static/d/556/path---index-6a9-L7r5Sntxcv3RUIoHYIR3Qqm9Jmg.json), but
then I want to dynamically shuffle that data and rearrange the DOM on render. Is that not possible with Gatsby? Do I need to give up the pre-fetched data during build and just consider that a dynamic component and fetch the
data via the Contentful API in componentDidMount?
If this approach should be OK, what am I doing wrong?
If this approach is not idomatic, is there a way to modify (shuffle) the data queried via graphql at build time? I'd actually be happier if the data only shuffled at build time and did not re-shuffle at run-time in the browser - I could just automate the site to rebuild every hour or so and the site could be more static to the client.
I've been struggling with this too recently!
My solution is to render the shuffled content once the parent component mounts using ReactDOM's render method:
import React, { useRef, useEffect } from "react";
import { render } from "react-dom";
import shuffle from "../utils/shuffle";
const shuffledArray = shuffle(array.slice());
// The below should still be able to work with graphql fetched data
// as I think the array will be saved to a variable for use in the client,
// although in my case I haven't used it so can't be fully sure
const ShuffledJSXElements = () =>
shuffledArray.map(creator => (
<li key={creator.id}>
<Link to={link}>
{creator.name}
</Link>
<img src={image} />
</li>
));
const Page = () => {
const shuffledContentContainerRef = useRef();
useEffect(() => {
const ontainer = portfolioContainerRef.current;
render(<ShuffledJSXElements />, container);
}, []);
return (
<MainWrapper>
<StyledPortfolioGridWrapper ref={shuffledContentContainerRef} />
</MainWrapper>
);
};
export default Portfolio;
One frustrating thing about this is that the container element won't have an awareness of its own height before the content is rendered, so the layout might jump about a bit. A workaround for this is to use a min-height css property.