I'm trying to create a chrome extension that clicks a button on a website using the DOM.click() method. (https://www.w3schools.com/jsref/met_html_click.asp)
EDIT: The purpose of this chrome extension is to create a keyboard shortcut to toggle on/off English Subtitles while watching a foreign language video. Having to use your mouse and dragging it to open a menu to turn on subtitles when you need them can be inconvenient if you are trying to understand the language without the subtitles. I wanted to create a keyboard shortcut that would immediately turn on the subtitles. An example of such a website is
(https://www.ondemandkorea.com/ask-us-anything-e102.html)
<button type="button" class="jw-reset jw-settings-content-item" role="menuitemradio" aria-checked="false">English</button>
This is button on the website I'm trying to click with Javascript
In my code, I have a window listener that waits for the specific website to load. Then, to find the button I want to click, I call document.getElementsByClassName("Class Name") and look through the returned elements array for a button that that says English and save it into var englishButton. I add another listener that listens for a keyboard key to be pressed which in turn presses englishButton.
However, when I click the shortcutKey, englishButton.click(); doesn't seem to do anything. I know that the correct English Button is found and that my shortcutKey Listener works through the use of console.log() statements.
I can't seem to understand why the button won't click.
EDIT: After adding a buttonListener to the code, the English button does click after all, but it does not turn on the subtitles for the video
Here's my code.
/*
Looking for the subtitle button that states "English"
*/
var englishButton;
window.addEventListener("load", function(event) {
var buttonList = document.getElementsByClassName('jw-reset jw-settings-content-item');
for (var i = 0, len = buttonList.length; i < len; i++){
if(buttonList[i].textContent === "English") {
englishButton = buttonList[i];
break;
}
}
englishButton.addEventListener('click', function() {
console.log('englishButton clicked!');
});
/*
Event Listener that detects when the shortcut key is hit.
When the shortcut Key is hit. It will simulate a mouse click on the subtitle button
*/
document.addEventListener('keyup', function(e){
if(e.key === shortcutKey){
console.log('shortcut pressed')
englishButton.click();
}
}
);
});
In your comments under your question, you confirmed that the button is actually triggering the click. So the issue for you is rather producing the intended result from the click. Which is to toggle on and off the English caption. There's a better, simpler, and much more reliable alternative here.
The website uses JW Player to show its video. They have a well-documented and open API (https://developer.jwplayer.com/jw-player/docs/developer-guide).
All you have to do is something like this
jwplayer().setCurrentCaptions(X)
Here X is the index number of the caption option you want to select from within the list of all captions that are available in a particular video.
In your example video, the list has only two items:
0: Off
1: English
So to turn on English:
jwplayer().setCurrentCaptions(1)
And to turn off all caption:
jwplayer().setCurrentCaptions(0)
If the index would vary from one video to another, you need to first get the list of captions available and then find the index number for English.
let allCaptions = jwplayer().getCaptionsList();
englishCaptionIndex = allCaptions.findIndex(caption => caption.label == 'English');
That's it.
You can do all kinds of interesting things using the API.
I recommend you to use jQuery, so you can use functions like keyup() or keydown() so you can listen when a key is pressed. Also a better practice is to check the element by id if we only want to watch over a DOM element instead of using a class.
Move all your code into your load listener:
https://codepen.io/ryanpcmcquen/pen/EOerPM?editors=1011
window.addEventListener("load", function(event) {
/*
Looking for the subtitle button that states "English"
*/
var englishButton;
// console.log('Website loaded. englishButton:', englishButton);
var buttonList = document.getElementsByClassName("jw-reset");
for (var i = 0, len = buttonList.length; i < len; i++) {
if (buttonList[i].textContent === "English") {
englishButton = buttonList[i];
// console.log("englishButton found", englishButton);
break;
}
}
// console.log("End of window-load's callback. englishButton:", englishButton);
/*
Event Listener that detects when the shortcut key is hit.
When the shortcut Key is hit. It will simulate a mouse click on the subtitle button
*/
document.addEventListener("keyup", function(e) {
console.log(
"Inside document-keyup's callback. englishButton:",
englishButton
);
if (e.key == "z") {
//Logic to press the subitle button
console.log(
"Key matched: ",
e.key,
"Now click button. englishButton:",
englishButton
);
englishButton.click();
console.log("Shortcut Key");
} else {
console.log("Random Key");
}
});
englishButton.addEventListener("click", function() {
console.log("englishButton clicked!");
});
});
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<button type="button" class="jw-reset">English</button>
</body>
</html>
But there are some things that could be improved in your code, so let's take a look at a more 'modernized' version of your code (comments in code):
// Using the `window` `load` event is fine, but
// you should prefer the `document` `DOMContentLoaded`
// event when possible, since it fires when the DOM
// has been constructed, while `load` means all assets have fully loaded (images).
// For your case since you are relying only on elements,
// `DOMContentLoaded` is a better choice.
document.addEventListener("DOMContentLoaded", function(event) {
/*
Looking for the subtitle button that states "English"
*/
var englishButton;
// Use `querySelectorAll` since it is more dynamic and
// will accept any type of selector. Also, for loops
// are avoided in most modern JavaScript because
// they create scoping and off-by-one errors.
// Since you want to break out of the loop here,
// we will use `.some()`. `Array.prototype.slice.call()`
// converts the NodeList returned by `querySelectorAll`
// into an Array.
Array.prototype.slice.call(document.querySelectorAll(".jw-reset")).some(
function (button) {
if (button.textContent === 'English') {
englishButton = button;
return true;
}
}
);
/*
Event Listener that detects when the shortcut key is hit.
When the shortcut Key is hit. It will simulate a mouse click on the subtitle button
*/
document.addEventListener("keyup", function(e) {
console.log(
"Inside document-keyup's callback. englishButton:",
englishButton
);
if (e.key === "z") {
//Logic to press the subitle button
console.log(
"Key matched: ",
e.key,
"Now click button. englishButton:",
englishButton
);
englishButton.click();
console.log("Shortcut Key");
} else {
console.log("Random Key");
}
});
englishButton.addEventListener("click", function() {
console.log("englishButton clicked!");
});
});
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<button type="button" class="jw-reset">English</button>
</body>
</html>
Related
I'm building this simple JavaScript project where if I input number N then it will show 1 to N. I'm using keyup() event for this. If I remove N from the input field then It will show nothing, which is fine! Because I'm using the empty() function. It works for 1-9. But when I input 10, 11, 12... it first shows 1 then it shows 1 to 10 or 1 to 11 accordingly. I only need to see 1 to N(more than single-digit). I don't want to use button for this.
Here is my code:
$(document).ready(function() {
$('#code').keyup(function() {
let myCode = $('#code').val();
if (myCode == '') {
$('#output').empty();
}
for (let i = 1; i <= myCode; i++) {
$('#output').append(i + '<br>');
}
})
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="number" id="code">
<br>
<br>
<div id="output"></div>
</body>
</html>
If this problem has better solution kindly share.
You need to use the concept of debouncing using setTimeout.
Your event is getting fired twice, once when you press '1' and when you press '0'. You need to hold the event until you are done with entering the numbers. The below code would work
var timer;
$(document).ready(function() {
$('#code').keyup(function() {
clearTimeout(timer)
timer = setTimeout(function(){let myCode = $('#code').val();
if (myCode == '') {
$('#output').empty();
}
for (let i = 1; i <= myCode; i++) {
$('#output').append(i + '<br>');
}},1000)
})
});
As Barmar pointed out in the comments, you need to reset the output every time the event is fired.
The keyup event happens immediately after the release a keyboard key.
So your listener function is executed every digit you enter (after the release of the key).
So if you insert 155, it first will append number to 1-1, then from 1-15 (because when you type the second digit and release the key (event fired) your input contains 15), and at last digit it will print number from 1- to the current input value that is 155 (input contains 155) and so on if you add digits.
Thus your code would be:
$(document).ready(function () {
$('#code').on('keyup', function () {
let myCode = $('#code').val();
// when event happens reset the output field, so it is overridden with the new serie
$('#output').empty();
for (let i = 1; i <= myCode; i++) {
$('#output').append(`${i}<br>`);
}
});
});
Small suggestions:
Use a "buffer" for the output because the append method is a bit expensive to call every cycle. Better build the string apart and append it when you're done.
Use the input event, it's more semantic and related to input tag. And the event is fired whenever the input content change, so it's more immediate.
$(document).ready(function () {
$('#code').on('input', function () {
let myCode = $('#code').val();
$('#output').empty();
let string = '';
for (let i = 1; i <= myCode; i++) {
string += `${i}<br>`;
}
$('#output').append(string);
});
});
function loop() {
MainBoard.moveSnake(); // moves the snake in the array
MainBoard.checkFood(); // checks for food and grows if there
MainBoard.drawSnake(); // refreshs canvas and draws updated snake
}
function main() {
MainBoard = new GameBoard();
MainBoard.generateFood();
MainBoard.drawSnake();
window.addEventListener("keyup", function(event) {
if (event.defaultPrevented) {
return; // Do nothing if the event was already processed
}
switch (event.key) {
case "ArrowDown":
// code for "down arrow" key press.
if (MainBoard.direction == 'N') {
} else {
MainBoard.direction = 'S';
MainBoard.loop();
}
break;
case "ArrowUp":
// code for "up arrow" key press.
if (MainBoard.direction == 'S') {
} else {
MainBoard.direction = 'N';
MainBoard.loop();
}
break;
case "ArrowLeft":
// code for "left arrow" key press.
if (MainBoard.direction == 'E') {
} else {
MainBoard.direction = 'W';
MainBoard.loop();
}
break;
case "ArrowRight":
// code for "right arrow" key press.
if (MainBoard.direction == 'W') {
} else {
MainBoard.direction = 'E';
MainBoard.loop();
}
break;
default:
return; // Quit when this doesn't handle the key event.
}
loop();
// Cancel the default action to avoid it being handled twice
event.preventDefault();
}, true);
}
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<link rel="stylesheet" type="text/css" href="Snake.css">
<link rel="stylesheet" type="text/css" href="Snake_Board.css">
<link rel="stylesheet" type="type/css" href="exit_button.css">
<body>
<h1 header="Snake" id="Snake">Snake</h1>
<p intro="Intro">Eat the food (Using the arrow keys) and don't hit your tail/go out of bounds!</p>
<canvas id="Board" name="Board"></canvas>
<!--<input type = "button" onclick = "main()" value = "Play"> -->
<!--<script type="text/javascript" src="SnakeScript.js"></script>-->
<script src="SnakeScript.js"></script>
<script type="text/javascript">
main()
</script>
</body>
</html>
Hi guys, I am writing a basic snake game into a webpage. I have it working and moving correctly; however, I can only figure out how to move it when a key is pressed. I want the snake to move independently every .75 seconds and have the user choose the direction. If the user didn't press anything it would move anyways with the default or current direction. I'm confused on how to do this with addEventListener(). It seems that it is always in the addEventListener() and never leaves it? Anyone have any hints? anything helps. Thanks!
Seems like you want to execute code with an interval.
setInterval(yourfunction, 3000);
Basically, create a variable that stores your direction, and with the interval, move the snake. There are a lot of great videos on YouTube of programming this game to check as well for some extra help.
Check out:
https://www.w3schools.com/jsref/met_win_setinterval.asp
Have the snake move on its own by constantly calling your MainBoard.loop() every 0.75 seconds using a setInterval(MainBoard.loop, 750);. Do so by taking it out of your addEventListener. Then to your window.addEventListener have it only change the direction and nothing else.
Edited:
I forgot to add a 'native' (for JS or browser) word to questions, as browsers (or JS i'm not sure) have a undo/redo feature in inputs, but it don't work with programmatically edited input and my main question is, if it is possible to add a code to trigger that native callback to previous value feature.
I tried to do this with document.execCommand paste/insertText but it didn't work and is marked as obsolete.
Old:
I have a custom action on keypress in input, for example where number have changed sign(+-) when '-' button is pressed.
I want to add that action to native undo/redo (ctrl+z/y) history stack, with preventing others default actions triggered when button responsible for my action is clicked.
Is this possible?
If no, to remove current, native undo/redo input feature, using event.preventDefault in crtl+z/y click detection would be enough? To replace it by custom undo/redo.
Is this dependable on browser?
I experimented with code on Firefox and Edge.
Is there something like History API for input available for JS and where i can read about it and how it is named in code?
As for similar topics in stack are old and i tried them but they didn't work (or i used them in wrong way).
$(document).ready(function() {
$(":input").on('keypress', function(e) {
//On minus button click, reverse sign for number in input:
if (e.key === '-') {
e.preventDefault();
//TODO: Add this change in input to undo/redo changes history:
$(this).val(-$(this).val());
}
});
});
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
Number: <input type="text" name="num"><br>
</body>
</html>
i3z slighty enchanted answer:
Ctrl+z instead of '-' button for Undo action.
Fixed bug with 2 clicks needed to use undo action after adding new element.
Sign change action from my question is added to code.
Added empty "" as default in history stack.
// Global Varaibles
// Edited: Added empty undo element, fixed undo working from 2nd click after adding new element to history.
var historyValues = [''];
var undoSteps = 2;
var maxSteps = 1;
var currentUndo = 1;
// Add event listener to track input and update historyValues
$(":input").on('input', function (evt) {
historyValues.push(this.value);
maxSteps = historyValues.length;
//console.log(historyValues);
// Check if 'Undo' been used if yes reset cause input been changed
if (undoSteps !== 2) {
undoSteps = 2;
}
});
$(document).ready(function() {
$(":input").on('keydown', function(e) {
if (undoSteps > maxSteps) {
// When you run out of backward steps, reset steps
undoSteps = 1;
}
// check for key (Edited: changed to z) and ensure 'currentUndo' not less than 0
if (e.ctrlKey && e.key === 'z' && currentUndo >= 0) {
e.preventDefault();
// tracking steps backward for every-time key '-' pressed
currentUndo = (maxSteps - undoSteps);
//console.log(maxSteps+' maxSteps - undoSteps '+undoSteps);
// Get previous value from history array
var newValue = historyValues[currentUndo];
//console.log(currentUndo+' current - value '+newValue);
$(this).val(newValue);
// Add +1 steps as we used undo once
undoSteps++;
}
});
$(":input").on('keypress', function(e) {
//Edited: Added custom action on key '-' press
if (e.key === '-') {
e.preventDefault();
$(this).val(-$(this).val());
//Trigger history
$(this).trigger('input');
}
});
});
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
Number: <input type="text" name="num"><br>
</body>
</html>
Simple Undo JS Script
You'll be able to capture event of (⌘ + z) or (CTRL + z). Therefore, you'll be able to preform this Undo JS implementation to have history stack of values every-time input changes.
You should have Event Listener (input) either via DOM or jQuery e.g.
$(":input").on('input', function (evt) {
console.log(this.value)
}
Now, you can trace any changes in input been targeted using jQuery Selector.
You should also define global variables inside your document, where you can track following:
History Stack (historyValues)
Steps To Take (undoSteps)
Maximum Steps (maxSteps)
Current Step (currentUndo)
Source Code
// Global Varaibles (Controls)
var historyValues = [''];
var undoSteps = 2;
var maxSteps = 1;
var currentUndo = 1;
// Add event listener to track input changes and update historyValues
$(":input").on('input', function (evt) {
historyValues.push(this.value);
maxSteps = historyValues.length;
//console.log(historyValues);
// Check if 'Undo' been used if yes reset cause input been changed
if (undoSteps !== 2) {
undoSteps = 2;
}
});
$(document).ready(function() {
$(":input").on('keydown', function(e) {
if (undoSteps > maxSteps) {
// When you run out of backward steps, reset steps
undoSteps = 1;
}
// Edited: Supports (⌘ + z and CTRL + z)
if ( (e.ctrlKey && e.key === 'z') || (e.metaKey && e.key === 'z') ) {
e.preventDefault();
// Ensure 'currentUndo' not less than 0
if (currentUndo >= 0) {
// tracking steps backward
currentUndo = (maxSteps - undoSteps);
// Get previous value from history array
var newValue = historyValues[currentUndo];
$(this).val(newValue);
// Add +1 steps as we used undo once
undoSteps++;
}
}
});
});
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
Number: <input type="text" name="num"><br>
</body>
</html>
I have a function that runs when the user presses the esc button:
$(document).on('keyup', function (e) {
if (e.keyCode == 27) {
foo();
}
});
I have a form with a file field.
<input type="file" />
When you click it, it opens a file-manager dialog, which can be closed with the esc as well. In that case, I want to avoid running the foo function. Is there a way to recognize it with javascript?
Here is some code that can do what you want (a complete test-page):
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>test-page</title>
<script type="text/javascript">
//<!--
var ignr=false;
function func(e)
{ if(!ignr)
{ //add any key-specific tests here
foo();
}
ignr = false;
return;
}
function foo()
{
return;
}
function func2(e)
{ //Could add a condition to do/not-do the next line, depending on the key...
ignr = true;
return;
}
// -->
</script>
</head>
<body onkeyup="func(event);">
<input type="file" onkeyup="func2(event);" />
</body>
</html>
The trick is to assign a different function to the input tag. This won't prevent the other function from being called, but the herein-specified func2() is called first, giving you the chance to set a flag-variable that can control what gets done in the "main" onkeyup function. Note while I didn't specify any tests for the Escape key, you can certainly add them, and even completely control which keys you want to allow "through" func2() to call foo() inside the main onkeyup function. At the moment, no keys pressed during the file-input will have any chance of calling foo().
So this is just a small personal project that I'm working on using awesomium in .net. So in awesomium I have this browser open and all that and I want to click this button that has this code.
<a class="buttonright" > Bump </a>
But considering it's a class and not a button I'm having trouble finding a way to "click" it. My plan is to use javascript in awesomium to click it but maybe I'm approaching this from the wrong direction?
Thanks
Update:
After a lot of comments (back and forth) I set up a fiddle, with a working version of this code (the code here works, too, but needed some debugging). The eventTrigger function in the fiddle has been stripped of all comments, but I've added an example usage of this function, which is generously sprinkled with comments.
Browse through it, fork it, play around and get familiar with the code and concepts used there. Have fun:
Here's the fiddle
If by "finding a way to click it" you mean: how to programmatically click this anchor element, then this is what you can use:
Here's a X-browser, slightly verbose yet comprehensive approach:
var eventTrigger = function(node, event)
{
var e, eClass,
doc = node.ownerDocument || (node.nodeType === (document.DOCUMENT_NODE || 9) ? node : document);
//after checking John Resig's Pro JavaScript Techniques
//the statement above is best written with an explicit 9
//Given the fact that IE doesn't do document.<NODE_CONSTANT>:
//doc = node.ownerDocument || (node.nodeType === 9 ? node : document);
if (node.dispatchEvent)
{//dispatchEvent method is present, we have an OK browser
if (event === 'click' || event.indexOf('mouse') >= 0)
eClass = 'MouseEvents';//clik, mouseup & mousedown are MouseEvents
else
eClass = 'HTMLEvents';//change, focus, blur... => HTMLEvents
//now create an event object of the corresponding class
e = doc.createEvent(eClass);
//initialize it, if it's a change event, don't let it bubble
//change events don't bubble in IE<9, but most browsers do
//e.initEvent(event, true, true); would be valid, though not standard
e.initEvent(event, !(event === 'change'), true);
//optional, non-standard -> a flag for internal use in your code
e.synthetic = true;//mark event as synthetic
//dispatch event to given node
node.dispatchEvent(e, true);
//return here, to avoid else branch
return true;
}
if (node.fireEvent)
{//old IE's use fireEvent method, its API is simpler, and less powerful
//a standard event, IE events do not contain event-specific details
e = doc.createEventObject();
//same as before: optional, non-standard (but then IE never was :-P)
e.synthetic = true;
//~same as dispatchEvent, but event name preceded by "on"
node.fireEvent('on' + event, e);
return true;//end IE
}
//last-resort fallback -> trigger any directly bound handler manually
//alternatively throw Error!
event = 'on' + event;
//use bracket notation, to use event's value, and invoke
return node[event]();//invoke "onclick"
};
In your case, you can use this function by querying the DOM for that particular element, like so:
var elem = document.querySelector('.buttonright');//IE8 and up, will only select 1 element
//document.querySelectorAll('.buttonright'); returns a nodelist (array-like object) with all elements that have this class
eventTrigger(elem, 'click');
That should have the effect of clicking the anchor element
If you're looking for a way to handle click events on this element (an anchor that has a buttonright class), then a simple event listener is all you need:
document.body.addEventListener('click', function(e)
{
e = e || window.event;
var target = e.target || e.srcElement;
if (target.tagName.toLowerCase() === 'a' && target.className.match(/\bbuttonright\b/))
{//clicked element was a link, with the buttonright class
alert('You clicked a button/link thingy');
}
}, false);
That's the cleanest way to do things (one event listener handles all click events). Of course, you can bind the handler to specific elements, too:
var buttons = document.querySelectorAll('.buttonright'),
handler = function(e)
{
alert('Clicked!');
};
for (var i=0;i<buttons.length;++i)
{
buttons[i].addEventListener('click',handler, false);
}
Depending on how you want to handle the event, there are numerous roads you can take.
The simplest one is this :
<script type="text/javascript">
function buttonRight_onclick(event, sender)
{
alert("HEY YOU CLICKED ME!");
}
</script>
<a class="buttonright" click="buttonRight_onclick(event, this)">
whereas if you were using a framework like jQuery, you could do it like this:
<script type="text/javascript">
$(document).ready(function() {
$(".buttonright").on("click", function(event) {
alert("HEY YOU CLICKED ME!");
});
});
</script>
<a class="buttonright" >Bump</a>
<a class="buttonright" >Also bump</a>
<script type="text/javascript">
function Button_onclick(event, sender)
{
alert("Button Clicked!");
}
</script>
<a class="Button" click="Button_onclick(event, this)">