I have a function that grabs an element and adds the class "is-open" in order to toggle an accordion. I have put this function in my "main.js".
In my FAQ component, this function needs to be called when a user clicks the accordion bar.
My question is: How do I import this main.js file within my Gatsby.js / React project?
I tried in the index or the component itself. But it is only called once I make changes to the main.js file. Obviously, I want it to be called right away when the page loads up.
This is the function within my main.js file:
const accordionEl = document.getElementsByClassName("faq-module--accordion-title--2zVOe")
if (accordionEl.length) {
for (let i = 0; i < accordionEl.length; i++) {
accordionEl[i].addEventListener("click", function() {
this.parentNode.classList.toggle("is-open")
const panel = this.nextElementSibling
if (panel.style.maxHeight) {
panel.style.maxHeight = null
} else {
panel.style.maxHeight = `${panel.scrollHeight}px`
}
})
}
}
Like I said, I have tried to add it like so in my FAQ component:
import "../js/main..js"
I have also tried to add it in index.html with tags.
You can put this in your footer (or some place that always loads):
<script src={`path/to/main.js`}></script>
Use the path to the file of the BUILT version. So you should probably put the script in "static/js/", and you should be able to import it from there.
But this is not really how you want to use Gatsby. Gatsby is built upon React, so for the reactive JavaScript, you should really be using React. It can't predict what the state of the DOM is if you start updating it behind its back. Furthermore this should be really easy to recreate in React with a simple onClick handler.
Related
This current piece of code:
<button onClick={() => setPageSection([p.slug+"/", s.slug])}>
makes my page jump to the start of the section when the images finish loading
In this case I have a grid of pictures that are correctly lazy loaded with an initial base64 lowres pre-render but everytime each one of the pictures finishes loading the page scrolls back to the top of the section.
This only happens when I use the setPageSection.
When I click a direct link to an anchor ("page/#section") of the page instead of using the above method of setting the page section the correct behavior happens (images continue lazy loading in the background and the pages doesn't snap to the top of the section everytime when each one finishes loading).
UPDATE:
The following looks like the culprit:
enter useEffect(() => {
if (typeof prevPageSection === 'undefined') return;
const scrollToSection = () => {
const el = document.getElementById(pageSection[1]);
if (scrollContainer.current && el) {
scrollContainer.current.scrollTo({ top: el.offsetTop });
}
};
if (pageSection[0] !== prevPageSection![0] && pageSection[0] !== pageContext.slug) {
navigate(locale + pageSection[0]);
scrollContainer.current?.addEventListener('load', scrollToSection, true);
}
scrollContainer.current?.removeEventListener('load', scrollToSection);
}, [pageSection, prevPageSection, locale, pageContext.slug]);
If I remove these event listeners the page stops jumping after the images load but the links get broken (they become useless when navigating to a section on a different page, without these event listeners one can only jump to the desired section if already on that page, otherwise the browser will navigate to the top of the page)
You have too many side effects in your useEffect hook. Each time one dependency (pageSection or prevPageSection or locale or pageContext.slug) is being changed, it triggers the hook, creating your unwanted effect:
const el = document.getElementById(pageSection[1]);
if (scrollContainer.current && el) {
scrollContainer.current.scrollTo({ top: el.offsetTop });
}
In addition, don't define a function inside the useEffect because it will cause a new scoped function creation each time the effect is triggered, overloading your code and application.
Moreover, don't point directly the DOM using document.getElementById(). It's a high-cost action for the browser and you can achieve the same effect using the useRef hook, you will have the exact same information using the .current property of the useRef hook, in the same way than you do the scrollContainer.current. Indeed, you are creating and manipulating a virtual DOM with React, to avoid pointing the DOM, while your code attacks the real DOM.
Be aware of the use of global objects such as window or document in Gatsby, they are not available during the SSR (Server-Side Rendering) so they may potentially break your code. To summarize, gatsby build compiles the code in your Node server, where there is no window or document because it's not even created yet. The code may work under gatsby develop because it's directly rendered by the browser.
To fix your issue, I would clean the useEffect, you can create as many effects as needed, and you can name them using this notation to make your code more readable:
useEffect(someNamedFunction(){
// your code
}, []).
To bypass it faster, you can create an auxiliary function that forces the page to scroll to certain element without involving the hooks:
const scrollToElement=(el)=>{
scrollContainer.current.scrollTo({ top: el.offsetTop });
}
<button onClick={(el)=> scrollToElement(el)}>
for my CSS framework customization needs I'm using a script for an onClick button "pulse effect". Currently I run it in mounted() part of my default.vue layout in Nuxt.js project running in SSR mode.
This is the script:
if (process.client) {
var elements = document.querySelectorAll(".button");
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener("click", function (event) {
event.preventDefault;
this.classList.remove("pulse");
void this.offsetWidth;
this.classList.add("pulse");
});
}
}
It's an easy script and it's working well but only on reload and only for some buttons which are on the top of my page's DOM.
When I copy-paste this script into a component's script part, it's working in the component well. But I do not want to include it into every component.
Where in my project should I add it to make it working in all components? I've tried to add it into the nuxt-config but it did not work.
Please help.
I would create a plugin that do that stuff for you you need to go to nuxt.config.js
plugins: ['~/plugins/pulse.js']
Now you go to your plugins folder and create that pulse.js and write in
import Vue from 'vue'
Vue.directive('pulse', {
inserted: (el) => {
el.addEventListener("click", function (event) {
event.preventDefault;
el.classList.remove("pulse");
void el.offsetWidth;
el.classList.add("pulse");
});
}
})
After that your directive is global and you can go to any button you want and add simply v-pulse to it
<button v-pulse>Click me</button>
Tell me if it works and if you receive any errors.
I have a project developed in Angular 6+, when user comes to website, first he sees logo and loading bar underneath it.
I have a problem switching to ngx-loading-bar insted of pace.js
Right now I am using Pace (the last example a fixed width line)
https://github.hubspot.com/pace/docs/welcome/
integrated into the index.html of the root project.
I want to put ngx-loading-bar instead of pace to do same job.
ngx-loading-bar is already implemented and working when adding <ngx-loading-bar></ngx-loading-bar> in some of the components but not in the index.html.
I want to put same style same width loading bar as on example link bellow (4th bullet example, similar to pace)
https://aitboudad.github.io/ngx-loading-bar/
so on the load, users sees the loading bar how it loads and on load, it goes on the next component as project example bellow.
Here is the example of project
www.wowlectures.com/pitchdeck
Not sure why you want it to work in index.html, do it in app.component.
You could create a loading service. A simple way is this:
#Injectable({providedIn: 'root'})
export class LoadingService {
private _loading = true;
get isLoading() {
return this._loading;
}
set isLoading(v: boolean) {
this._loading = v;
}
}
Then in app.component.ts:
export class AppComponent {
constructor(public loading: LoadingService) {}
}
And app.component.html:
<any-loading-component *ngIf="loading.isLoading"></any-loading-component>
<router-outlet></router-outlet>
A cleaner way is to use subjects/observables in the service and subscribe to changes where you want to show the loading-bar and dispatch from anywhere you want it to start loading.
<app-root>
<ngx-loading-bar></ngx-loading-bar>
</app-root>
hey you can add this in your index.html
I'm trying to create a ES6 module that can be initialized multiple times (once for each instance of an on-page element).
I have a page with 3 video components on it, I want to toggle the class of 'playing' on the video that the click came from. However only the instance of the last video is ever having the class toggled.
Could someone please help..? I can't for the life of me get the code running for each individual instance.
index.html
<div class="video">...</div>
<div class="video">...</div>
<div class="video">...</div>
main.js
import * as video from './video';
var $videos = $('.video');
$.each($videos, function(i, $video) {
video.build($video);
});
video.js
export function build (videoInstance) {
$(videoInstance).on('click', function (e) {
$(this).toggleClass('playing');
});
}
When a user changes the theme, I use mainWindow.webContents.send to change a class in the DOM. I also save it in the store, under the key theme.
mainWindow.webContents.send('theme:change', theme);
store.set('theme', theme);
Then in renderer.js:
ipcRenderer.on('theme:change', (event, theme) => {
document.querySelector('body').className = `${theme}`;
});
This successfully changes the theme and saves it in the store. However, now I want that theme to load up when starting the application rather than going back to the default. To do this, in app.on('ready') I am doing this:
mainWindow.webContents.send('theme:change', store.get('theme'));
However, nothing is happening. It's like it isn't being sent. Where am I going wrong? Essentially what needs to be done is for the class in body to be changed when the application loads to the one in the store.
Figured it out. I had to put:
mainWindow.webContents.once('dom-ready', () => {
mainWindow.webContents.send('theme:change', store.get('theme'));
})
I was trying mainWindow.on('dom-ready') which is why it wasn't working.