You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vanilla JavaScript实现LinkedIn自动消息发送文本框赋值失败问题

解决LinkedIn自动消息脚本无法插入文本框的问题

问题描述

我用原生JavaScript编写的LinkedIn自动消息发送脚本,除了无法将消息文本插入至输入框外,其余操作(如点击发送按钮等)均按预期执行。问题出在pasteNote函数中,控制台日志显示文本内容符合预期,但实际并未在文本框中显示。已尝试修改querySelector,问题仍未解决。

相关代码

Linkedin = {
    config: {
        scrollDelay: 2000,
        actionDelay: 3000,
        nextPageDelay: 5000,
        // 设置为-1表示无限制
        maxRequests: -1,
        totalRequestsSent: 0,
        // 设置为false跳过邀请中的备注
        // addNote: true,
        note: "Dear {{name}}, thank you for sharing your professional network."
    },
    // 初始化脚本,延迟后调用scrollBottom
    init: function (data, config) {
        console.info("INFO: script initialized on the page...");
        console.debug("DEBUG: scrolling to bottom in " + config.scrollDelay + " ms");
        setTimeout(() => this.scrollBottom(data, config), config.actionDelay);
    },
    // 平滑滚动到页面底部,延迟后调用scrollTop
    scrollBottom: function (data, config) {
        window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
        console.debug("DEBUG: scrolling to top in " + config.scrollDelay + " ms");
        setTimeout(() => this.scrollTop(data, config), config.scrollDelay);
    },
    // 平滑滚动回页面顶部,延迟后调用inspect
    scrollTop: function (data, config) {
        window.scrollTo({ top: 0, behavior: 'smooth' });
        console.debug("DEBUG: inspecting elements in " + config.scrollDelay + " ms");
        setTimeout(() => this.inspect(data, config), config.scrollDelay);
    },
    // 检查页面搜索结果,调用compile处理
    inspect: function (data, config) {
        var totalRows = this.totalRows();
        console.debug("DEBUG: total search results found on page are " + totalRows);
        if (totalRows >= 0) {
            this.compile(data, config);
        } else {
            console.warn("WARN: end of search results!");
            this.complete(config);
        }
    },
    // 筛选页面上的"Message"按钮,获取联系人姓名
    compile: function (data, config) {
        var elements = document.querySelectorAll('button');
        data.pageButtons = [...elements].filter(function (element) {
            return element.textContent.trim() === "Message";
        });
        if (!data.pageButtons || data.pageButtons.length === 0) {
            console.warn("ERROR: no message buttons found on page!");
            console.info("INFO: moving to next page...");
            setTimeout(() => { this.nextPage(config) }, config.nextPageDelay);
        } else {
            data.pageButtonTotal = data.pageButtons.length;
            console.info("INFO: " + data.pageButtonTotal + " message buttons found");
            data.pageButtonIndex = 0;
            var names = document.getElementsByClassName("entity-result__title-text");
            names = [...names].filter(function (element) {
                return element.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.textContent.includes("Message\n");
            });
            data.connectNames = [...names].map(function (element) {
                return element.innerText.split(" ")[0];
            });
            console.debug("DEBUG: starting to send messages in " + config.actionDelay + " ms");
            setTimeout(() => this.sendMessages(data, config), config.actionDelay);
        }
    },
    // 点击Message按钮,延迟后调用pasteNote
    sendMessages: function (data, config) {
        console.debug("remaining requests " + config.maxRequests);
        if (config.maxRequests == 0) {
            console.info("INFO: max requests reached for the script run!");
            this.complete(config);
        } else {
            console.debug('DEBUG: sending invite to ' + (data.pageButtonIndex + 1) + ' out of ' + data.pageButtonTotal);
            var button = data.pageButtons[data.pageButtonIndex];
            button.click();
            console.debug("DEBUG: pasting Message in popup, in " + config.actionDelay + " ms");
            setTimeout(() => this.pasteNote(data, config), config.actionDelay);
        }
    },
   
    // 插入消息文本的函数(原问题所在)
    pasteNote: function (data, config) {
        console.log("start of pasteNote" + noteTextBox);
        noteTextBox = document.querySelector("[class^='msg-form__msg-content-container--scrollable']");
        console.log(noteTextBox.value);
        noteTextBox.value = config.note.replace("{{name}}", data.connectNames[data.pageButtonIndex]);
        noteTextBox.dispatchEvent(new Event('input', {
            bubbles: true
        }));
        window.onload = noteTextBox.value;
        console.log("end of pasteNote" + noteTextBox.value);

        console.debug("DEBUG: clicking send in popup, if present, in " + config.actionDelay + " ms");
        setTimeout(() => this.clickDone(data, config), config.actionDelay);
    },
    // 点击发送按钮
    clickDone: function (data, config) {
        var buttons = document.querySelectorAll('button');
        var doneButton = Array.prototype.filter.call(buttons, function (el) {
            return el.textContent.trim() === 'Send';
        });
        if (doneButton && doneButton[0]) {
            console.debug("DEBUG: clicking send button to close popup");
            doneButton[0].click();
        } else {
            console.debug("DEBUG: send button not found, clicking close on the popup in " + config.actionDelay);
        }
        setTimeout(() => this.clickClose(data, config), config.actionDelay);
    },
    // 关闭弹出框,处理下一个联系人或下一页
    clickClose: function (data, config) {
        var closeButton = document.getElementsByClassName('artdeco-modal__dismiss artdeco-button artdeco-button--circle artdeco-button--muted artdeco-button--2 artdeco-button--tertiary ember-view');
        if (closeButton && closeButton[0]) {
            closeButton[0].click();
        }
        console.info('INFO: invite sent to ' + (data.pageButtonIndex + 1) + ' out of ' + data.pageButtonTotal);
        config.maxRequests--;
        config.totalRequestsSent++;
        if (data.pageButtonIndex === (data.pageButtonTotal - 1)) {
            console.debug("DEBUG: all connections for the page done, going to next page in " + config.actionDelay + " ms");
            setTimeout(() => this.nextPage(config), config.actionDelay);
        } else {
            data.pageButtonIndex++;
            console.debug("DEBUG: sending next invite in " + config.actionDelay + " ms");
            setTimeout(() => this.sendMessages(data, config), config.actionDelay);
        }
    },
    // 跳转到下一页
    nextPage: function (config) {
        var pagerButton = document.getElementsByClassName('artdeco-pagination__button--next');
        if (!pagerButton || pagerButton.length === 0 || pagerButton[0].hasAttribute('disabled')) {
            console.info("INFO: no next page button found!");
            return this.complete(config);
        }
        console.info("INFO: Going to next page...");
        pagerButton[0].children[0].click();
        setTimeout(() => this.init({}, config), config.nextPageDelay);
    },
    // 脚本完成提示
    complete: function (config) {
        console.info('INFO: script completed after sending ' + config.totalRequestsSent + ' messages');
    },
    // 获取页面搜索结果数量
    totalRows: function () {
        var search_results = document.getElementsByClassName('search-result');
        if (search_results && search_results.length != 0) {
            return search_results.length;
        } else {
            return 0;
        }
    }
}

Linkedin.init({}, Linkedin.config);

控制台输出

INFO: script initialized on the page... debugger eval code:18:17
DEBUG: scrolling to bottom in 2000 ms debugger eval code:19:17
undefined
DEBUG: scrolling to top in 2000 ms debugger eval code:26:17
DEBUG: inspecting elements in 2000 ms debugger eval code:32:17
DEBUG: total search results found on page are 0 debugger eval code:40:17
INFO: 1 message buttons found debugger eval code:61:21
DEBUG: starting to send messages in 3000 ms debugger eval code:70:21
remaining requests -1 debugger eval code:76:17
DEBUG: sending invite to 1 out of 1 debugger eval code:81:21
DEBUG: pasting Message in popup, in 3000 ms debugger eval code:86:21
start of pasteNote[object HTMLDivElement] debugger eval code:100:17
undefined debugger eval code:104:17
end of pasteNote Dear Valerio, I'm Valerio, thank you for sharing your professional network. debugger eval code:110:17
DEBUG: clicking send in popup, if present, in 3000 ms debugger eval code:112:17
DEBUG: clicking send button to close popup debugger eval code:122:21
INFO: invite sent to 1 out of 1 debugger eval code:134:17
DEBUG: all connections for the page done, going to next page in 3000 ms debugger eval code:142:21
INFO: no next page button found! debugger eval code:154:21
INFO: script completed after sending 1 messages debugger eval code:165:17

问题分析与修复

核心问题

从控制台输出start of pasteNote[object HTMLDivElement]可以明确:你选中的noteTextBox是一个外层Div容器,而非真正的输入元素。LinkedIn的消息输入区域是采用contenteditable="true"的Div实现的,这类元素没有value属性,直接赋值value不会生效。

修复后的pasteNote函数

pasteNote: function (data, config) {
    console.log("start of pasteNote");
    // 定位真正的contenteditable输入框
    const noteTextBox = document.querySelector("[contenteditable='true'].msg-form__contenteditable");
    if (!noteTextBox) {
        console.error("ERROR: 未找到消息输入框");
        return;
    }
    // 生成替换姓名后的消息
    const message = config.note.replace("{{name}}", data.connectNames[data.pageButtonIndex]);
    // 清空原有内容并设置新消息
    noteTextBox.textContent = '';
    noteTextBox.textContent = message;
    // 触发输入和变更事件,确保LinkedIn前端框架识别内容变化
    noteTextBox.dispatchEvent(new Event('input', { bubbles: true }));
    noteTextBox.dispatchEvent(new Event('change', { bubbles: true }));
    // 模拟焦点操作,更贴近真实用户行为
    noteTextBox.focus();
    console.log("end of pasteNote: " + noteTextBox.textContent);

    console.debug("DEBUG: clicking send in popup, if present, in " + config.actionDelay + " ms");
    setTimeout(() => this.clickDone(data, config), config.actionDelay);
}

额外优化建议

  • 选择器稳定性:LinkedIn的类名可能会动态变更,建议结合contenteditable属性和父容器层级来定位元素,避免依赖易变的类名前缀。
  • 错误处理:添加元素存在性判断,防止脚本因找不到输入框而中断执行。
  • 动态监听替代固定延迟:可以使用MutationObserver监听弹出框的加载状态,待输入框完全渲染后再执行输入操作,比固定延迟更可靠,避免因页面加载慢导致的元素未就绪问题。

内容的提问来源于stack exchange,提问作者Valerio Viale

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 23:11:14