Change innerHTML in electron window - javascript

A pretty basic electron question, explaining it by a specific example:
in my first electron app I want to change the innerHTML of a label before sync-copying a larger file.
here's what I wrongfully thought the solution is. with this code, the label text is changed after the file was copied:
renderer.js:
const lbl = document.getElementById('statusLabel')
lbl.innerHTML = "New Description"
fs.copyFileSync(...)
However, I can "force" first updating the label by this:
renderer.js:
const lbl = document.getElementById('statusLabel')
lbl.innerHTML = "New Description"
ipcRenderer.send('copyFile')
main.js:
ipcMain.on('copyFile', () => {
ipcMain.send('copyFile'))
}
I could replace fs.copyFileSync with any other function - the point is, I only get it to work in the required order by sending the subsequent action to the renderer.
is this intended electron behaviour or did I miss something basic?

Related

Injecting gatsby-image into html created from markdown using ReactDOMServer.renderToString

Minimum Reproducible Example on Github
I'm trying to inject some images into my pages created from markdown. I'm trying to do this using ReactDomServer.renderToString()
const componentCreatedFromMarkdown = ({data}) => {
...
useEffect(() => {
const injectDivs = Array.from(document.getElementsByClassName('injectDivs'))
injectDivs.forEach((aDiv) => {
aDiv.innerHTML = ReactDOMServer.renderToString(<Img fluid={data.allFile.edges[0].node.childImageSharp.fluid} />)
}
})
...
}
The img is showing as a black box, if I right click the image I can open it in a new tab, which shows the image as it is supposed to be.
How I understand the problem
The image html is correctly inserted into the page
gatsby-image loads the lowest quality image and applies some inline
styles. All that information is present in the html) This is done to
enable the blur-up effect on the image for a better UX.
The client side code that would load a higher resolution image and remove the inline styles is never applied.
Useful diagnostic information
The function inside useEffect does not get run on the server-side, but rather on the client after the component has mounted. It's possible to verify this by adding a console.log statement inside the effect and seeing that it is logged on the browser's console.
Source of the problem
ReactDOMServer.renderToString() only builds the HTML that's required by a component, it then requires the component to be hydrated.
Direct fix
You can use ReactDOM.render to put the image on the page, given that the effect will execute client side. I have a PR showing that here.
Recommended approach
You could instead import the image inside the mdx component and directly render it there. Your gatsby config can already support this πŸ‘ In content/blog/hello-world/index.mdx, you can replace the injectImage div with this ![A salty egg, yummm](./salty_egg.jpg) (where the text inside the [] is the alt text)
This documentation might be helpful
https://www.gatsbyjs.org/docs/working-with-images-in-markdown/
https://www.gatsbyjs.org/packages/gatsby-remark-images/
Hope that helps! πŸ˜„
I have faced this problem before with my first Gatsby project.
The problem similar to this chat and the error image issue here.
If you replace GatsbyImageSharpFixed_withWebp_tracedSVG like they talked in spectrum chat above, or this is my current code for example, basically is related to WebP new image format.
export const query = graphql`
query {
allImageSharp {
edges {
node {
fluid(maxWidth: 110) {
aspectRatio
originalName
sizes
src
srcSet
srcWebp
tracedSVG
}
}
}
}
}
`;
Hope this help.
I think the problem could be in the useEffect
const componentCreatedFromMarkdown = ({data}) => {
...
useEffect(() => {
const injectDivs = Array.from(document.getElementsByClassName('injectDivs'))
injectDivs.forEach((aDiv) => {
aDiv.innerHTML = ReactDOMServer.renderToString(<MyComponent args={myArgs} />)
}
},[])
...
}
I think you should try to add useEffect(()=>{},[]), the second argument to refresh the image only when the component is mounted, without this the image will be refreshed each time.

How to initialize value of CodeMirror binding to yjs?

My main problem is initializing the text/value of a code editor(CodeMirror) on my website without it affecting the way I save/send POST requests to my backend. The following is the pug code I use for the POST request:
p
form(id='form' method='POST', action='/docs/edit/'+docs._id)
textarea(name="doo" id="content" style="display: none;")=docs.content
textarea(name="foo" id="editortext" style="display: none;")
input.btn.btn-primary(type='submit' value='Save Doc')
What I'm trying to do here, is send docs.content to textarea with id "content" so that I can use that to initialize the value of my code editor and then put the content of whats in the code editor in the textarea
"editortext" once I click the submit button. Thus, the POST request would fetch me the data from both textareas, where I can then save the content of the "editortext" textarea to my database. The logic of the code editor is referenced in the same pug file to a javascript file after rollup transpilation. The following is a chunk of the pre-compiled code:
/* eslint-env browser */
import * as Y from 'yjs'
import { WebsocketProvider } from 'y-websocket'
import { CodeMirrorBinding } from 'y-codemirror'
import CodeMirror from 'codemirror'
import 'codemirror/mode/clike/clike.js'
window.addEventListener('load', () => {
const ydoc = new Y.Doc()
const provider = new WebsocketProvider(
`${location.protocol === 'http:' ? 'ws:' : 'wss:'}${location.host}`,
'codemirror',
ydoc
)
const yText = ydoc.getText('codemirror')
const editorContainer = document.createElement('div')
editorContainer.setAttribute('id', 'editor')
document.body.insertBefore(editorContainer, null)
let content = document.getElementById("content").value
const editor = CodeMirror(editorContainer, {
mode: 'text/x-java',
lineNumbers: true
})
editor.setValue(content)
document.getElementById("form").onsubmit = function(evt){
document.getElementById("editortext").value = editor.getValue();
}
Most of this code is from the yjs-codemirror demo except for the declaration of the content variable,the invocation of the setValue method, and the document.getElementById("form") block. What this code currently does is send me the right information to my database. However, I am having trouble initializing the value of the code editor when I open up the document. The setValue method doesn't work, neither does doing the following:
const editor = CodeMirror(editorContainer, {
value: content,
mode: 'text/x-java',
lineNumbers: true
})
All of the prior examples fail even if I replace the content variable with some string. The only thing that seems to work is the following:
const editor = CodeMirror(editorContainer, {
mode: 'text/x-java',
lineNumbers: true
}).setValue(content)
However, the problem with this is that for some reason, I get the following errors in the console browser:
TypeError: codeMirror is undefined (y-codemirror.js:160:4)
TypeError: editor is undefined (index.js:28:10)
For reference, the javascript that I have been showing in this question was all from the index.js file. In any case, because the editor is undefined, I can no longer set the value of my "editortext" textarea to the CodeMirror Textarea and I can't save what is written to the code editor to my database. I'm not sure as to why this would happen, I'm not sure if this is particular to the CodeMirrorBinding from yjs but any help on this would be massively appreciated.
The following is quoted from dmonad who is one of the developers of Yjs. For future reference regarding any technical questions about Yjs, you will probably get better luck asking here as there isn't a tag for Yjs yet on StackOverflow.
Hi #notemaster,
I assume that you mean you are unable to set the value of the CodeMirror editor.
The CodeMirrorBinding binds the value of the Y.Text type to a CodeMirror instance. The setValue method works, but the value of the editor is overwritten by the binding:
ytext.insert(0, 'ytext')
const editor = CodeMirror(..)
editor.setValue('my value')
editor.value() // => "my value"
new CodeMirrorBinding(editor, ytext)
editor.value() // => "ytext value"
I suggest that you set the value after it has been bound to the YText type.
Another note: There is nothing like a default value in Yjs. Initially, the Yjs document is empty until it synced with the server. So you might want to wait until the client synced with the server before setting the value.
const setDefaultVal = () => {
if (ytext.toString() === '') {
ytext.insert(0, 'my default value')
}
}
if (provider.synced) {
setDefaultVal()
} else {
provider.once('synced', setDefaultVal)
}
const editor = CodeMirror(editorContainer, {
mode: 'text/x-java',
lineNumbers: true
}).setValue(content)
I assume editor.setValue() returns undefined . This is why the binding won’t work and you can set the initial value of the editor.

Data is present in clipboard, but I can't get it in JavaScript

I know this question was asked before, I read almost everything. The problem I have is, that value that I copied is available when pressing ctrl+v, but not when trying window.getSelection.getData().
const copyToClipboard = (value) => {
const temp = document.createElement('input');
const body = document.getElementsByTagName('body')[0];
body.appendChild(temp);
temp.value = value;
temp.select();
document.execCommand('copy');
body.removeChild(temp);
};
Now, when I try ctrl+v, the pasted value is correct. When I try to use window.getSelection.getData(), there is no data.
But when I comment out the last line (body.removeChild(temp)), then both ctrl+v and window.getSelection.getData() works correctly. But I have an element in the dom.
Is there a way to get this value in code? I need this for tests in Nightwatch.

iBooks Author/iAd Producer Widget User Text Disappears

I'm using iAd producer to create HTML widgets for iBooks I'm making using iBooks Author. I've done some research and learned that I can have user input saved into local storage by way of a variable that is called every time the widget is opened. This is great except for the new problem of having to create hundreds of text box widgets all with different variables so I can use these text boxes on multiple pages. Is there a way for me to automate this using Java Script? One idea I had was to use a "while" function to tell the script to ++ the variable if the one it tried to use was not empty. Example: the variable "001" was already used so the code would ideally set the next user text to variable "002". Preferably, I'd like to be able to create one widget with this code that I could reuse anywhere else.
Here is the code I'm currently using:
/*Widget code*/
this.onViewControllerViewWillAppear = function (event) {
var theValue = this.outlets.textField;
if (localStorage.getItem("theKey102") === null) {
theValue.value = "";
} else {
theValue.value = localStorage.getItem("theKey102");
}
};
/*This is the code for one of the text boxes I'm using */
this.onControlValueChange = function (event) {
var theValue = this.viewController.outlets.textField;
localStorage.setItem("theKey102", theValue.value);
};

onClick replace /segment/ of img src path with one of number of values

No idea what I'm doing or why it isn't working. Clearly not using the right method and probably won't use the right language to explain the problem..
Photogallery... Trying to have a single html page... it has links to images... buttons on the page 'aim to' modify the path to the images by finding the name currently in the path and replacing it with the name of the gallery corresponding to the button the user clicked on...
example:
GALLERY2go : function(e) {
if(GalleryID!="landscapes")
{
var find = ''+ findGalleryID()+'';
var repl = "landscapes";
var page = document.body.innerHTML;
while (page.indexOf(find) >= 0) {
var i = page.indexOf(find);
var j = find.length;
page = page.substr(0,i) + repl + page.substr(i+j);
document.body.innerHTML = page;
var GalleryID = "landscapes";
}
}
},
There's a function higher up the page to get var find to take the value of var GalleryID:
var GalleryID = "portfolio";
function findGalleryID() {
return GalleryID
}
Clearly the first varGalleryID is global (t'was there to set a default value should I have been able to find a way of referring to it onLoad) and the one inside the function is cleared at the end of the function (I've read that much). But I don't know what any of this means.
The code, given its frailties or otherwise ridiculousness, actually does change all of the image links (and absolutely everything else called "portfolio") in the html page - hence "portfolio" becomes "landscapes"... the path to the images changes and they all update... As a JavaScript beginner I was pretty chuffed to see it worked. But you can't click on another gallery button because it's stuck in a loop of some sort. In fact, after you click the button you can't click on anything else and all of the rest of the JavaScript functionality is buggered. Perhaps I've introduced some kind of loop it never exits. If you click on portfolio when you're in portfolio you crash the browser! Anyway I'm well aware that 'my cobbled together solution' is not how it would be done by someone with any experience in writing code. They'd probably use something else with a different name that takes another lifetime to learn. I don't think I can use getElement by and refer to the class/id name and parse the filename [using lots of words I don't at all understand] because of the implications on the other parts of the script. I've tried using a div wrapper and code to launch a child html doc and that come in without disposing of the existing content or talking to the stylesheet. I'm bloody lost and don't even know where to start looking next.
The point is... And here's a plea... If any of you do reply, I fear you will reply without the making the assumption that you're talking to someone who really hasn't got a clue what AJAX and JQuery and PHP are... I have searched forums; I don't understand them. Please bear that in mind.
I'll take a stab at updating your function a bit. I recognize that a critique of the code as it stands probably won't help you solve your problem.
var currentGallery = 'landscape';
function ChangeGallery(name) {
var imgs = document.getElementsByTagName("img") // get all the img tags on the page
for (var i = 0; i < imgs.length; i++) { // loop through them
if (imgs[i].src.indexOf(currentGallery) >= 0) { // if this img tag's src contains the current gallery
imgs[i].src = imgs[i].src.replace(currentGallery, name);
}
}
currentGallery = name;
}
As to why I've done what I've done - you're correct in that the scope of the variables - whether the whole page, or only the given function, knows about it, is mixed in your given code. However, another potential problem is that if you replace everything in the html that says 'landscape' with 'portfolio', it could potentially change non-images. This code only finds images, and then replaces the src only if it contains the given keyword.

Categories