【问题标题】:How to access html DOM element through electron js如何通过电子js访问html DOM元素
【发布时间】:2022-02-25 00:04:04
【问题描述】:

我正在使用电子 js 制作文本编辑器,一旦用户按下 ctrl + s,我希望将文件保存为 txt 文件。但问题是,我似乎找不到直接访问包含文本的 div 的方法。我试过使用预加载,但只有在程序运行后才有效。如何将元素作为变量保存?

以下是主要的 javascript 代码:

const { app, BrowserWindow, globalShortcut } = require('electron');
const path = require('path');

// Create the main window
const createWindow = () => {

  // Adjust a few settings
  const win = new BrowserWindow({
    // What the height and width that you open up to
    width: 500,
    height: 600,

    // Minimun width and height
    minWidth: 400,
    minHeight: 400,

    icon: __dirname + '/icon.png',
    
    // Change the window title
    title: "text editor",

    webPreferences: {
      // Preload so that the javascript can access the text you write
      preload: path.join(__dirname, 'preload.js'),
    }    
  });
  
  win.loadFile('index.html');

  // Remove that ugly title bar and remove unnecessary keyboard shortcuts
  win.removeMenu();
}

// Create window on ready so that no nasty errors happen
app.whenReady().then(() => {
  createWindow();
});

app.whenReady().then(() => {

  // Global shortcut so the user has the ablitiy to exit
  globalShortcut.register('ctrl+e', () => {
    console.log("exiting...");
    app.exit();
  });

  globalShortcut.register('ctrl+s', () => {
    console.log("saving...");
  });
})


// when all windows close this app actually closes
app.on('window-all-closed', () => {
  if (process !== 'darwin') app.quit();
})

【问题讨论】:

    标签: javascript node.js electron


    【解决方案1】:

    要在index.html 窗口中获取div 元素的innerText(或等效元素),您需要向渲染线程发送一条消息以请求此信息。在此之后,您将需要您的渲染线程将innerText 发送回您的主线程进行处理(保存)。

    Electron 的 Inter-Process Communication 有时可能会让人感到困惑,但如果实施得当,它会变得简单而安全。

    要了解有关所涉及过程的更多信息,您需要阅读并尝试理解以下链接:


    让我们首先构建您的 html 文档。它至少必须包含一个可编辑的<div> 标签和一个“保存”按钮。

    index.html(渲染线程)

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8" />
            <title>Test Editor</title>
            <style>
                #editor {
                    width: 50vw;
                    height: 50vh;
                }
            <style>
        </head>
    
        <body>
            <div id="content" contenteditable="true"></div>
            <input type="button" id="save" value="Save">
        </body>
    
        <script src="script.js"></script>
    </html>
    

    请参阅Example: A simple but complete rich text editor 了解一些很酷的想法。


    现在让我们添加“保存”按钮和 IPC 消息功能。

    script.js(渲染线程)

    // IIFE (Immediately Invoked Function Expression)
    (function() => {
        let content = document.getElemetById('content').innerText;
    
        document.getElementById('save').addEventListener('click', saveContent(content));
    
        window.ipcRender.receive('editor:getContent', () => { saveContent(content); });
    });
    
    function saveContent(content) {
        window.ipcRender.send('editor:saveContent', content);
    }
    

    这是您的 main.js 文件,其中包含以下更新。

    • 添加 Electron 的 ipcMain 模块。
    • 将win 对象添加到顶级范围,这样它就不会被垃圾回收。
    • 侦听来自渲染线程的消息(使用 IFFE)。
    • 添加saveContent() 函数(由您完全充实)。
    • 从new BrowserWindow 行中删除const。
    • 从createWindow() 函数返回win,以便以后可以引用它。
    • 更新globalShortcutctrl+s函数。

    main.js(主线程)

    const { app, BrowserWindow, globalShortcut, ipcMain } = require('electron');
    const path = require('path');
    
    let win = null;
    
    // IIFE (Immediately Invoked Function Expression)
    (function() => {
      ipcMain.on('editor:saveContent', (event, content) => { saveContent(content); });
    })();
    
    function saveContent(content) {
      console.log("saving...");
      // Save content...
      console.log("saved...");
    }
    
    // Create the main window
    function createWindow() {
    
      // Adjust a few settings
      win = new BrowserWindow({
        // What the height and width that you open up to
        width: 500,
        height: 600,
    
        // Minimun width and height
        minWidth: 400,
        minHeight: 400,
    
        icon: __dirname + '/icon.png',
        
        // Change the window title
        title: "text editor",
    
        webPreferences: {
          // Preload so that the javascript can access the text you write
          preload: path.join(__dirname, 'preload.js'),
        }    
      });
      
      win.loadFile('index.html');
    
      // Remove that ugly title bar and remove unnecessary keyboard shortcuts
      win.removeMenu();
    
      return win;
    }
    
    // Create window on ready so that no nasty errors happen
    app.on('ready', () => {
      // Create the window.
      win = createWindow();
    
      // Global shortcut so the user has the ability to exit
      globalShortcut.register('ctrl+e', () => {
        console.log("exiting...");
        app.exit();
      });
    
      // Global shortcut to save editable content.
      globalShortcut.register('ctrl+s', () => {
        console.log('ctrl+s pressed.');
        win.webContents.send('editor:getContent');
      });
    })
    
    // when all windows close this app actually closes
    app.on('window-all-closed', () => {
      if (process !== 'darwin') app.quit();
    })
    

    请注意,我已将文件系统功能的实际保存留给您。请参阅Node.js: fs.writeFile() 了解更多信息。


    好的,最后一块拼图是一个有效的preload.js 脚本。这是允许在主线程和渲染线程之间使用白名单通道列表的脚本。

    在这里我们添加editor:saveContent 和editor:getContent 频道名称。

    preload.js(主线程)

    const contextBridge = require('electron').contextBridge;
    const ipcRenderer = require('electron').ipcRenderer;
    
    // White-listed channels.
    const ipc = {
      'render': {
        // From render to main.
        'send': [
          'editor:saveContent'
        ],
        // From main to render.
        'receive': [
          'editor:getContent'
        ],
        // From render to main and back again.
        'sendReceive': []
      }
    };
    
    contextBridge.exposeInMainWorld(
      // Allowed 'ipcRenderer' methods.
      'ipcRender', {
        // From render to main.
        send: (channel, args) => {
          let validChannels = ipc.render.send;
          if (validChannels.includes(channel)) {
            ipcRenderer.send(channel, args);
          }
        },
        // From main to render.
        receive: (channel, listener) => {
          let validChannels = ipc.render.receive;
          if (validChannels.includes(channel)) {
            // Deliberately strip event as it includes `sender`
            ipcRenderer.on(channel, (event, ...args) => listener(...args));
          }
        },
        // From render to main and back again.
        invoke: (channel, args) => {
          let validChannels = ipc.render.sendReceive;
          if (validChannels.includes(channel)) {
            return ipcRenderer.invoke(channel, args);
          }
        }
      }
    );
    

    请注意,我没有在preload 脚本中执行任何所谓的功能。我只管理一个列表 频道名称以及与这些频道名称相关的任何数据的传输。

    【讨论】:

    • 谢谢 :D,回复是彻底而清晰的。
    猜你喜欢
    • 2015-12-23
    • 1970-01-01
    • 2017-12-16
    • 1970-01-01
    • 2011-01-16
    • 2017-05-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多