I'm trying to create an Electron app that has multiple "pages".
In my case, I'm trying to make an app with a sidebar that has different sections. Once a section is clicked, the main window's content changes to render the appropriate content for the section.
I'm new to JS so sorry if this is a dumb question, but as of now, whenever I try to go to a section of the app, I get a white-flash screen for a second before everything loads again.
Example: https://i.imgur.com/qOyuYsz.gif
I know this has to do with Electron reloading the Chrome engine, but how can I make it so when a section is clicked, the content is displayed automatically without any "flashes" or weird things?
Basically: how can I build a GUI with lots of components using Electron?
My code for my index.html is:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Rupture Tools</title>
<link rel="stylesheet" href="./photon/css/photon.min.css">
</head>
<body>
<div class="window">
<div class="window-content">
<div class="pane-group">
<div class="pane-sm sidebar">
<nav class="nav-group">
<h5 class="nav-group-title">1 Click Generator</h5>
<a class="nav-group-item active">
<span class="icon icon-home"></span>
Dashboard
</a>
<a href="accounts.html">
<span class="nav-group-item">
<span class="icon icon-user-add"></span>
Accounts
</span>
</a>
<span class="nav-group-item">
<span class="icon icon-cloud-thunder"></span>
Activity
</span>
<span class="nav-group-item">
<span class="icon icon-check"></span>
Check Scores
</span>
<span class="nav-group-item">
<span class="icon icon-cog"></span>
Settings
</span>
<span class="nav-group-item">
<span class="icon icon-help-circled"></span>
Help/FAQ
</span>
</nav>
</div>
<div class="pane">Home</div>
</div>
</div>
</div>
</body>
</html>
Please help! I'm clueless at this, been searching everywhere. I come from Python where there isn't much of any front-end development or GUI designing. Thanks!
There is "sort of" a solution here, but it uses something called Sass and as far as I know using something like React or Angular is better. I've never used either of those.
Electron apps are very similar to web apps. The traditional way of navigating between HTML documents doesn't work well for apps as you noticed. That's why web apps are developed as single-page applications (SPA) nowadays. It simply means loading and replacing parts of the page manually using JavaScript when the user navigates. There are several ways to implement this, but here's an example how it could be done for your code:
// Get all the navigation links to an array
const naviLinks = Array.from(document.getElementsByClassName("nav-group-item"));
const contentEl = document.getElementsByClassName("pane")[0];
naviLinks.forEach((linkEl) => {
// Listen click event for the navigation link
linkEl.addEventListener("click", e => {
// Prevent default behavior for the click-event
e.preventDefault();
// Get the path to page content file
const href = linkEl.getAttribute("href");
if (href) {
// Use node.js fs-module to read the file
const fs = require("fs");
fs.readFile(href, (err, data) => {
if (err) {
throw err;
}
// show the selected page
contentEl.innerHTML = "";
contentEl.insertAdjacentHTML("beforeend", data);
})
}
})
})
Note that the page content HTML files (accounts.html etc.) should only have the content for the "pane" div. You also need to pass nodeIntegration:true when creating your BrowserWindow-object in the main-process, so you can use require to load the fs-module:
new BrowserWindow({
webPreferences: {
nodeIntegration: true
}
}
If the page content files are large, navigation may seem slow, because files are read and pages are rendered on every click. One optimization to help with that is to read files and create page elements off-screen already at page load and then just swap the elements on click-events. Alternatively you could put the page contents in <template>-elements and swap them. I'll leave these for you to try out by yourself, if you're interested.
There are loads of JavaScript frameworks that can help you with creating SPAs. Some popular ones at the moment are React, Angular and Vue. "How can I build a GUI with lots of components?" is one of the questions front-end JavaScript frameworks can answer, but there's of course a learning curve. When you feel the need to start splitting your GUI into reusable or hierarchical components, it's probably a good idea to look into those JavaScript frameworks.
I made some code a while back to do this (Unfortunately it's rather complicated but it shouldn't be so hard to implement it).
You put this function on every page:
function loadPageWithIframe (url) {
var hiddenPage = document.createElement("iframe");
hiddenPage.setAttribute("src", url);
hiddenPage.style.display = 'none';
document.body.appendChild(hiddenPage);
hiddenPage.onload = function () {
var frameDocument = hiddenPage.document;
if (hiddenPage.contentDocument) {
frameDocument = hiddenPage.contentDocument;
} else if (hiddenPage.contentWindow) {
frameDocument = hiddenPage.contentWindow.document;
}
document.open();
document.write(frameDocument.documentElement.innerHTML);
document.close();
window.history.pushState("", document.title, url.replace('https://' + window.location.hostname, ''));
}
}
And this code in your Electron file:
mainWindow.webContents.on('will-navigate', function (evt, url) {
evt.preventDefault();
mainWindow.webContents.executeJavaScript('loadPageWithIframe("' + url + '");');
});
And if you put the code in correctly it should work automatically, without any extra code.
The way this works is you call the loadPageWithIframe function when you want to go to a url, then it makes an iframe and loads the page, copies the all the html from the iframe and overwrites the current page's HTML with the iframes HTML.
But instead of calling the loadPageWithIframe function on manually on every click you can use Electron's will-navigate event to let us know that it's going to another page, then call the loadPageWithIframe (this is the purpose of the electron code that I posted).
I hope that Helps :)
Related
I am working on a school project and I have to do a static digital menu website for a bar. Because it's static, I used JavaScript where necessary. Anyways, I divided everything into groups, each group is represented by a card with an image and a button. Here is an example:
This is the source code for a card:
<body>
<div id="cards">
<div class="card">
<img src="/Resources/Food.png" class="card_image">
<a href="javascript:showMenu()" class="button">
<p>FOOD</p>
</a>
</div>
</div>
</body>
As you can see, in order to add a single card, I had to manually write the whole structure of a card in the second div, with the class="card".
BUT, I must create it dynamically based on the structure of the subfolders inside the Root folder witch is called Resources, here's a scheme:
In order to achieve this I started using JavaScript since it's the only possible way I think:
<script type="text/javascript">
function showMenu()
{
var content = `
<div class="card">
<img src="/Resources/Food.png" class="card_image">
<a href="javascript:showMenu()" class="button">
<p>FOOD</p>
</a>
</div>`;
document.querySelector("#cards").innerHTML = content;
}
</script>
So, now that I've expressed what I need to do is this: in the JavaScript code you can see that the card was generated manually anyways but I need the content to generate based on the folder structure I have stored locally. In other words, the whole script should take in input the name/path of the ROOT folder which is called "Resources" and from there it should generate the groups based on it's content. For example, if I click the button on the FOOD Card, then it should delete the FOOD and DRINKS Cards and only add the Vegetables Card in this case...I know it sounds complicated but at the end of the day the problem lies in getting the subfolder names, and since the image has the same name of the subfolder, apply it on the <img> tag and also on the button. All this, using JavaScript. If you know that some other language would work much better I'm open to suggestions, but I built the entire website until now only using JavaScript.
Anyways, I tried to express the problem the best I could so if something is unclear, I can easily modify the post if needed. Thanks in advance!
You could put the image data into a JSON object, then bind related processing functions according to the requirement.
I'm trying to use Tampermonkey to add a popup on pages in the Canvas LMS. It's a forum, and after each post there is a "Reply" option, which is what I want to add the popup to. But when I click the "Reply" link, no popup appears. It opens the Reply box, as normal, but my popup is nowhere to be seen.
The code looks roughly like this:
<div class="entry-controls hide-if-collapsed hide-if-replying">
<div class="notification" data-bind="notification"></div>
<a role="button" class="discussion-reply-action entry-control" data-event="addReply" href="#">
<i class="icon-replied"></i>
<span aria-hidden="true">Reply</span>
<span class="screenreader-only">Reply to Comment</span>
</a>
</div>
The JS code I'm trying to add is:
document.querySelectorAll('.discussion-reply-action').forEach(item => {
item.addEventListener('click', event => {
alert("Popup text here");
})
})
In addition to .discussion-reply-action, I've tried using .entry-controls, .notification, .entry-control, even stuff like span[aria-hidden="true"]. Nothing seems to work.
I know the Tampermonkey script itself is applying correctly, because it has other functionality that is showing up as usual.
Any idea why this bit isn't working for me? I'm a complete JS noob, for what that's worth.
This got answered in the replies, but just wanted to formally note that it came down to delaying my code injection. I was trying to attach to elements that loaded after the doc. Once I got behind them, it worked fine.
I have a client site that has a community events page. On that page, events are generated and put out one by one like this:
<div class="event-info">
<h5>This is an event</h5>
<span class="date">December 25, 2013</span>
<p class="details"></p>
</div>
They want a link on the homepage that goes to this one Christmas event, which will eventually start to move down the page. The only way to really find that block is by the contents of the <H5>. A classic anchor link would do the trick, but I can't add them to the <div class="event-info" /> block. The homepage link has to go to this page and then jump down to the necessary <div class="event-info" />.
jQuery or vanilla javascript are all I have to work with. I have one .js file that I can add to, and of course the html/javascript of the link itself on the homepage. I can't manipulate the existing HTML of the pages (content is dynamic - I add new stuff to it), and I have no access to the backend .NET framework.
Thanks.
something like this?
jquery:
var hash = window.location.hash;
if (hash==='#xmas') {
var xmas = $('.date:contains("December 25")').parent('.event-info').offset().top;
$('html,body').animate({scrollTop: xmas}, 500);
}
You just add #xmas to end of the page url, like: http://fiddle.jshell.net/filever10/afK7M/show/light/#xmas
made a fiddle: http://jsfiddle.net/filever10/afK7M/
I have been searching about this subject now for quite a few days. And could not find a working or conclusive answer.
What I want to do, is to simply display the (styled) summary of the latest blog entry (from the blog page on my own site) in a div container on the front page of my site (which is not my blog). All active links of that mirrored blog entry ideally lead to the appropriate section of my blog page. That is however not a must, as long as the entire entry can link to the blog page.
Each blog entry summary on the blog summary page has a unique ID, sorted by numbers (e.g. unique-ID-51 (latest) unique-ID-50 (the one before) etc.)
I was thinking of doing so with the document.getElementById JS command.
I would have to point the JS function to a relative location (../blog_folder/blog_summary.html) with maybe the .window.location.assign
command, than grab the (styled) contents of the latest element and display that on my front page.
But I have no idea how that code would look in reality. Can you point me in the right direction?
Thank you !!!!!!!
M.
You could add jQuery to your page and use a simple construction:
$('.result-container').load('path/to/your/file.html #id_of_element_to_fetch');
An example chunk of code:
...
<body>
<div class="result-container">There will be your content from some file.</div>
<p>
<a class="result-loader" href="#"></a>
<script type="text/javascript">
$(".result-loader").click(function() {
//Replace path/to/your/file.html and #id_of_element_to_fetch with appropriate values
$('.result-container').load('path/to/your/file.html #id_of_element_to_fetch');
return false;
});
</script>
</p>
</body>
...
And that string somewhere inside the <head> tag:
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js"></script>
An example chunk of code with an autostart feature:
...
<body>
<div class="result-container">There will be your content from some file.</div>
<p>
<a class="result-loader" href="#"></a>
<script type="text/javascript">
$(document).ready(function() { //Launches the code below right after the initialization event
//Replace path/to/your/file.html and #id_of_element_to_fetch with appropriate values
$('.result-container').load('path/to/your/file.html #id_of_element_to_fetch');
return false;
});
</script>
</p>
</body>
...
I assume you are using a hidden iframe?
This for example will thet the height of the style.. there are other things in the style
this.container.getElementsByTagName("YOUuniqueID")[0].style.(STYLE)
But you have to put an unique ID in the iframe
Try and use the built in debuggers in IE or Chrome to find what you want...
You can take a look at this for maybe some more info(its for cross domain) but there could be something taht helps you. You might even consider using jquery to access that data.
Yet Another cross-domain iframe resize Q&A
This is more like a auto-click link problem. But my problem is this link is generate by google's script.
http://translate.google.com/translate_tools
If you choose "translate a section" , there will be a link generate inside the goog-trans-control class
Original script:
<div class="goog-trans-section">
<div class="goog-trans-control">
</div>
Original Text here.
</div>
Script code after execute (Check Component):
<div class="goog-trans-section">
<div class="goog-trans-control">
<div class="skiptranslate goog-te-sectional-gadget-link" style="">
<div id=":1.gadgetLink">
<a class="goog-te-gadget-link" href="javascript:void(0)">
<span class="goog-te-sectional-gadget-link-text">Translate</span>
</a>
</div>
</div>
</div>
Original Text here.
</div>
How would I auto-click (or execute) the Translate link after this page is totally loaded?
For some reason, jsfiddle is not working with my script, though I still post this for your convenience.
http://jsfiddle.net/Wb7tE/
Really appreciate for your time and help.
Edited:
I tried Google translate API, but there is a limitation of 5000 words at a time.
My translations include whole html with tables and scripts, so it reach the limit with no exception.
I have a similar problem, and I solved it temporally like this
google_initialized = false;
function google_auto_translate()
{
if(google_initialized)
{
$('a.goog-te-gadget-link')[0].click();
}
else if(google.translate)
{
google_initialized = true;
setTimeout(google_auto_translate, 500);
}
else
setTimeout(google_auto_translate, 100);
}
window.onload = google_auto_translate;
but on slower connection, in 50 % of time google doesn't load on time, and script already clicks before loading is done. So if anyone know any other way to do this, via some events or something similar please add it here...
P.S. Don't use Google Translation API it's Deprecated and will be removed till the end of this year.