innerHTML 不会在每个 keydown 事件中改变
innerHTML won't change in each keydown event
以下代码旨在将一个元素(myNote
)的innerHTML更改为另一个元素(myInput
)的值,每次 Return 键被按下:
// ==UserScript==
// @name Duolingo
// @include *://duolingo.com/*
// @include *://www.duolingo.com/*
// ==/UserScript==
setTimeout(()=>{
let myInput = document.querySelector('._7q434._1qCW5._2fPEB._3_NyK._1Juqt._3WbPm');
let myNote = document.createElement('div');
document.body.appendChild(myNote);
myNote.setAttribute("style", "position: fixed; bottom: 0; right: 0; display: block; width: 400px; height: 400px; background: orange; color: #000");
myInput.addEventListener('keydown', (k)=>{
if ( k.keyCode === 13 ) {
myNote.innerHTML += myInput.value + '<br>';
}
});
}, 2000);
目的
我 运行 duolingo.com(一个学习 mind2mind 语言如法语的网站)上的 Greasemonkey 代码,在 "translate this sentence".
这样的文本问题会话中
该代码的目的是创建一个橙色背景的小框,其中包含我已经在 Duolingo 问题中尝试过的输入,前提是 Duolingo 不会保存这些内容。
有了脚本,我可以保存它们,并在我重考语言问题时使用它们,因为它节省了我重新输入大部分句子的时间。
问题
代码失败,因为 innerHTML
只改变了一次。如果值再次改变,我按下 Return,什么也不会发生。
重现
在 duolingo.com.
上使用 Greasemonkey(或类似程序)中的代码进行复制
问题
为什么 innerHTML
只更改一次?鉴于 addEventListener
一直在监听,为什么它只工作一次?
使用 return
进行更改或添加 return false
没有帮助。
也许需要另一种添加包含每次按下的值的新元素的方法。
Mobius 更新:
Mobius,这是我使用的代码,但没有用:
setTimeout(()=>{
window.myCss =`.note {position: fixed; bottom: 0; right: 0; width: 400px; height: 400px; background: orange}`;
style = document.createElement("style");
style.type = "text/css";
style.styleSheet ? style.styleSheet.cssText = myCss : style.appendChild(document.createTextNode(myCss) );
head = document.head || document.getElementsByTagName("head")[0];
head.appendChild(style);
let note = document.createElement('div');
note.classList.add('note');
document.querySelector('body').appendChild(note);
let savedValue;
document.addEventListener('keydown', (e)=>{
let target = e.target;
if (target.nodeName === 'textarea') {
savedValue = e.target.value;
}
});
if (k.keyCode === 13) {
setTimeout(()=>{
document.querySelector('textarea').value = savedValue;
}, 100);
}
}, 2500);
我在控制台没有任何错误。
我试着阅读这篇文章,我明白在最初的例子中我是胡说八道而不是捕获(我应该通过从 html
元素到我的 textarea
元素来捕获任何处理程序每次重新捕获它的处理程序,但我看不出如何改进代码。也许我现在心情不好,所以没读好...
据我所知,duolingo 中的测验是用 React 编写的,因此很可能您添加事件监听器的元素不再存在。有几种方法可以解决这个问题,但最好的方法可能是使用事件冒泡(可能在捕获阶段)来监听不会被替换的内容,例如 body
元素并等待来自元素的事件符合您的需求。
您的代码将如下所示:
let savedValue = '';
document.addEventListener('keydown', (e) => {
let target = e.target;
if (target.nodeName == 'TEXTAREA') {
// save off the text value;
savedValue = e.target.value;
}
if (e.keyCode == 13){
setTimeout(() => {
document.querySelector('textarea').value = savedValue;
}, 10);
}
}, true)
// ==UserScript==
// @name Duolingo
// @namespace nms
// @include *://duolingo.com/*
// @include *://www.duolingo.com/*
// @version 1
// @grant none
// ==/UserScript==
setTimeout(()=>{
// Style:
/*let myCss =`
.note {position: fixed; bottom: 0; right: 0; width: 400px; height: 400px; background: orange}
`;*/
let myCss =`
.note {position: fixed; top: 100px; left:100px; bottom: 100px; right: 100px; width: 200px; height: 200px; background: orange}
`;
style = document.createElement("style");
style.type = "text/css";
style.styleSheet ? style.styleSheet.cssText = myCss : style.appendChild(document.createTextNode(myCss) );
head = document.head || document.getElementsByTagName("head")[0];
head.appendChild(style);
// Behavior:
let myNote = document.createElement('div');
myNote.classList.add('note');
document.querySelector('body').appendChild(myNote);
let myInputField = document.querySelector('textarea');
myInputField.addEventListener('keydown', (k)=>{
if ( k.keyCode === 13 ) {
myNote.innerHTML = myInputField.value;
}
});
}, 2000);
<textarea></textarea>
<br>
<div id="myNote">
</div>
如果您在 Whosebug 上 运行 完整页面视图中的原始代码,代码 运行 没问题。我更改了橙色矩形的 css 以在非完整页面视图中更好地查看结果并且它也有效。注意:我使用的是 Google Chrome 版本 49.0.2623.112 m。
代码失败(在删除拼写错误后),因为它将事件侦听器添加到文本区域,网页会被每个新问题覆盖。当文本区域被覆盖时,它会破坏您添加的先前事件侦听器。它还在该代码中孤立了 myInput
的值。
在持久容器或整个主体上使用更智能的事件侦听器。并且,根据需要刷新 myInput
。
还有其他问题:
._7q434._1qCW5._2fPEB._3_NyK._1Juqt._3WbPm
是一个特别脆弱的选择器,下次网站代码更改时会崩溃。它在我看到的某些页面上也已经不起作用。
textarea[data-test]
可能会起作用。考虑到其余代码,甚至 textarea
可能就足够了。
setTimeout
是一种等待元素的脆弱方式,是 already causing you grief。使用 waitForKeyElements
或 MutationObserver
或类似的代替。但在您的情况下,由于您正在等待用户交互,您可能不需要任何其他延迟机制。
将所有这些放在一起,您的用户脚本将类似于:
// ==UserScript==
// @name Duolingo, answer text recycler
// @match *://*.duolingo.com/*
// @run-at document-idle
// ==/UserScript==
let myNote = document.createElement ('div');
document.body.appendChild (myNote);
myNote.setAttribute (
"style",
"position: fixed; bottom: 0; right: 0; width: 400px; height: 400px; background: orange; overflow: auto;"
);
document.addEventListener ('keydown', (zEvent) => {
if (zEvent.keyCode === 13) {
if (zEvent.target.nodeName === 'TEXTAREA') {
let myInput = document.querySelector ('textarea[data-test]');
if (myInput) {
myNote.innerHTML += myInput.value + '<br>';
}
}
}
} );
以下代码旨在将一个元素(myNote
)的innerHTML更改为另一个元素(myInput
)的值,每次 Return 键被按下:
// ==UserScript==
// @name Duolingo
// @include *://duolingo.com/*
// @include *://www.duolingo.com/*
// ==/UserScript==
setTimeout(()=>{
let myInput = document.querySelector('._7q434._1qCW5._2fPEB._3_NyK._1Juqt._3WbPm');
let myNote = document.createElement('div');
document.body.appendChild(myNote);
myNote.setAttribute("style", "position: fixed; bottom: 0; right: 0; display: block; width: 400px; height: 400px; background: orange; color: #000");
myInput.addEventListener('keydown', (k)=>{
if ( k.keyCode === 13 ) {
myNote.innerHTML += myInput.value + '<br>';
}
});
}, 2000);
目的
我 运行 duolingo.com(一个学习 mind2mind 语言如法语的网站)上的 Greasemonkey 代码,在 "translate this sentence".
这样的文本问题会话中该代码的目的是创建一个橙色背景的小框,其中包含我已经在 Duolingo 问题中尝试过的输入,前提是 Duolingo 不会保存这些内容。
有了脚本,我可以保存它们,并在我重考语言问题时使用它们,因为它节省了我重新输入大部分句子的时间。
问题
代码失败,因为 innerHTML
只改变了一次。如果值再次改变,我按下 Return,什么也不会发生。
重现
在 duolingo.com.
上使用 Greasemonkey(或类似程序)中的代码进行复制问题
为什么 innerHTML
只更改一次?鉴于 addEventListener
一直在监听,为什么它只工作一次?
使用 return
进行更改或添加 return false
没有帮助。
也许需要另一种添加包含每次按下的值的新元素的方法。
Mobius 更新:
Mobius,这是我使用的代码,但没有用:
setTimeout(()=>{
window.myCss =`.note {position: fixed; bottom: 0; right: 0; width: 400px; height: 400px; background: orange}`;
style = document.createElement("style");
style.type = "text/css";
style.styleSheet ? style.styleSheet.cssText = myCss : style.appendChild(document.createTextNode(myCss) );
head = document.head || document.getElementsByTagName("head")[0];
head.appendChild(style);
let note = document.createElement('div');
note.classList.add('note');
document.querySelector('body').appendChild(note);
let savedValue;
document.addEventListener('keydown', (e)=>{
let target = e.target;
if (target.nodeName === 'textarea') {
savedValue = e.target.value;
}
});
if (k.keyCode === 13) {
setTimeout(()=>{
document.querySelector('textarea').value = savedValue;
}, 100);
}
}, 2500);
我在控制台没有任何错误。
我试着阅读这篇文章,我明白在最初的例子中我是胡说八道而不是捕获(我应该通过从 html
元素到我的 textarea
元素来捕获任何处理程序每次重新捕获它的处理程序,但我看不出如何改进代码。也许我现在心情不好,所以没读好...
据我所知,duolingo 中的测验是用 React 编写的,因此很可能您添加事件监听器的元素不再存在。有几种方法可以解决这个问题,但最好的方法可能是使用事件冒泡(可能在捕获阶段)来监听不会被替换的内容,例如 body
元素并等待来自元素的事件符合您的需求。
您的代码将如下所示:
let savedValue = '';
document.addEventListener('keydown', (e) => {
let target = e.target;
if (target.nodeName == 'TEXTAREA') {
// save off the text value;
savedValue = e.target.value;
}
if (e.keyCode == 13){
setTimeout(() => {
document.querySelector('textarea').value = savedValue;
}, 10);
}
}, true)
// ==UserScript==
// @name Duolingo
// @namespace nms
// @include *://duolingo.com/*
// @include *://www.duolingo.com/*
// @version 1
// @grant none
// ==/UserScript==
setTimeout(()=>{
// Style:
/*let myCss =`
.note {position: fixed; bottom: 0; right: 0; width: 400px; height: 400px; background: orange}
`;*/
let myCss =`
.note {position: fixed; top: 100px; left:100px; bottom: 100px; right: 100px; width: 200px; height: 200px; background: orange}
`;
style = document.createElement("style");
style.type = "text/css";
style.styleSheet ? style.styleSheet.cssText = myCss : style.appendChild(document.createTextNode(myCss) );
head = document.head || document.getElementsByTagName("head")[0];
head.appendChild(style);
// Behavior:
let myNote = document.createElement('div');
myNote.classList.add('note');
document.querySelector('body').appendChild(myNote);
let myInputField = document.querySelector('textarea');
myInputField.addEventListener('keydown', (k)=>{
if ( k.keyCode === 13 ) {
myNote.innerHTML = myInputField.value;
}
});
}, 2000);
<textarea></textarea>
<br>
<div id="myNote">
</div>
如果您在 Whosebug 上 运行 完整页面视图中的原始代码,代码 运行 没问题。我更改了橙色矩形的 css 以在非完整页面视图中更好地查看结果并且它也有效。注意:我使用的是 Google Chrome 版本 49.0.2623.112 m。
代码失败(在删除拼写错误后),因为它将事件侦听器添加到文本区域,网页会被每个新问题覆盖。当文本区域被覆盖时,它会破坏您添加的先前事件侦听器。它还在该代码中孤立了 myInput
的值。
在持久容器或整个主体上使用更智能的事件侦听器。并且,根据需要刷新 myInput
。
还有其他问题:
._7q434._1qCW5._2fPEB._3_NyK._1Juqt._3WbPm
是一个特别脆弱的选择器,下次网站代码更改时会崩溃。它在我看到的某些页面上也已经不起作用。textarea[data-test]
可能会起作用。考虑到其余代码,甚至textarea
可能就足够了。setTimeout
是一种等待元素的脆弱方式,是 already causing you grief。使用waitForKeyElements
或MutationObserver
或类似的代替。但在您的情况下,由于您正在等待用户交互,您可能不需要任何其他延迟机制。
将所有这些放在一起,您的用户脚本将类似于:
// ==UserScript==
// @name Duolingo, answer text recycler
// @match *://*.duolingo.com/*
// @run-at document-idle
// ==/UserScript==
let myNote = document.createElement ('div');
document.body.appendChild (myNote);
myNote.setAttribute (
"style",
"position: fixed; bottom: 0; right: 0; width: 400px; height: 400px; background: orange; overflow: auto;"
);
document.addEventListener ('keydown', (zEvent) => {
if (zEvent.keyCode === 13) {
if (zEvent.target.nodeName === 'TEXTAREA') {
let myInput = document.querySelector ('textarea[data-test]');
if (myInput) {
myNote.innerHTML += myInput.value + '<br>';
}
}
}
} );