I have a button that is purposely designed to open multiple pages at once, it is the main feature of the button.
I tried using:
(1)
urls.forEach(url => {
window.open(url);
});
(2) promises with a delay on them but that did not work either.
(3) multiple a tags and trying to simulate a human click however, that did not work either.
var element = document.createElement("a");
element.href = tempUrl;
element.innerHTML = "temp";
element.id = "tempAtag";
element.target = "_blank";
document.getElementById("dashboardID").appendChild(element);
element = document.getElementById("tempAtag");
var box = element.getBoundingClientRect(),
coordX = box.left + (box.right - box.left) / 2,
coordY = box.top + (box.bottom - box.top) / 2;
var simulateMouseEvent = function (element, eventName, coordX, coordY) {
element.dispatchEvent(
new MouseEvent(eventName, {
view: window,
bubbles: true,
cancelable: true,
clientX: coordX,
clientY: coordY,
//button: 0,
})
);
};
simulateMouseEvent(element, "mousedown", coordX, coordY);
simulateMouseEvent(element, "mouseup", coordX, coordY);
simulateMouseEvent(element, "click", coordX, coordY);
This does work for the first link but I get a warning in the console saying I am trying to open multiple tabs with only one interaction. So it works for one link but not for the rest.
I got the code from Simulate a REAL HUMAN mouse click in pure javascript?
(Note: I did delete my a tags after each iteration and I did test it out, there are no duplicates)
I ran out of ideas. Any ideas?
(I have looked at other solutions and none of what I came across has worked for me)
The following works with a big IF attached.
That if is you need to click the "allow pop-up" warning that will appear in the address bar and continue to allow pop-ups from that site.
You also need to have pop-ups to load in a new tab.
I still think this is a duplicate question with Open a URL in a new tab (and not a new window)
var id = 0, u = ['12','34','56','78'];
function openNextTab(){
if (u[id]){
var x = window.open("https://some.site/id:"+ u[id],"id"+u[id]);
id++;
setTimeout(openNextTab,2000);
}
}
function openTabs(e){
e.stopPropagation();e.preventDefault();
openNextTab();
}
window.onload = function(){
var b = document.getElementById("openLinks");
b.addEventListener("click",openTabs,false);
}
Related
I am developing an iOS application using Swift that incorporates web content by using WKWebView.
[URL Loading and Goal] Once a URL has been loaded inside this web view, I want to open a keyboard whenever the user wants to type.
[JavaScript Events] As browsing is based on eye coordinates, I want a JavaScript event to be triggered when the user's gaze is focused on an input field. On the basis of eye gaze coordinates, I can determine the underlying element of the webpage document. After this, I want to trigger a mouse event/touch event for clicks within the input element.
However, the system-level keyboard does not appear if I follow these steps.
Code:
touchPoint variable contains the current x and y coordinates of the eye gaze.
#IBOutlet var webView : WKWebView!
let jsStyle = """
function sendTouchEvent(x, y, element, eventType) {
const touchObj = new Touch({
identifier: Date.now(),
target: element,
clientX: x,
clientY: y,
radiusX: 2.5,
radiusY: 2.5,
rotationAngle: 10,
force: 0.5,
});
const touchEvent = new TouchEvent(eventType, {
cancelable: true,
bubbles: true,
touches: [touchObj],
targetTouches: [],
changedTouches: [touchObj],
shiftKey: true,
});
element.dispatchEvent(touchEvent);
}
function click(x, y)
{
var ev = new MouseEvent('click', {
'view': window,
'bubbles': true,
'cancelable': true,
'screenX': x,
'screenY': y
});
var el = document.elementFromPoint(x, y);
if(el.nodeName == "INPUT") {
el.autofocus = true;
el.click();
el.focus();
sendTouchEvent(x, y, el, 'touchstart');
}
try {
el.dispatchEvent(ev);
} catch(error) {
console.log(error);
}
}
click(\(touchPoint.x), \(touchPoint.y));
"""
webView.evaluateJavaScript(jsStyle, completionHandler : { result, error in
print("### Evaluate JavaScript for current click!")
} => Called when eye gaze coordinates are received.
[Temporary Solution] In order to make this work, I created a native UITextField that acts as a proxy for the webpage element and opens the keyboard by becoming the first responder when I determine using JS that the eye gaze is focused on an input element.
My goal, however, is to remove the proxy logic and replace it with JavaScript code that opens the system-level keyboard.
I am trying to develop a chrome plugin that will record a series of click and play them back
I have seen that you can simulate a click on a DOM element but the problem is in my case, I only have a webGL canvas and the buttons are not directly accessible
I have managed to get the user's click position using this :
document.onclick = function(e)
{
var x = e.pageX;
var y = e.pageY;
console.log(x + " " + y)
//browser.runtime.sendMessage({"message": "open_new_tab", "url": "aze"});
};
But I haven't found anything to use these positions to perform a click action on the page
If I understand your question properly, you want to simulate a click on the webpage?
JavaScript has the click() method exposed for all elements:
element.click()
If you don't know the element, but just the positions:
document.elementFromPoint(x, y).click()
You can dispatch a mouse click at a specific position:
const = (x, y) => {
const ev = new MouseEvent('click', {
'view': window,
'bubbles': true,
'cancelable': true,
'screenX': x,
'screenY': y
})
const el = document.elementFromPoint(x, y);
el.dispatchEvent(ev);
}
All the above should be done via Content Script within the extension.
On an installed Desktop PWA, since Chrome 73 (on MacOS) when I do a window.open() on a click event with a regular URL, the page loads fully but the window is blank.
It looks obfuscated but there's no overlay tag of anything visible (everything is OK in the devtools's console and network tabs)
I tried with the default Chrome theme, both with Mojave's dark an light mode.
The HTML markup:
<a href="https://jakearchibald.github.io/svgomg/" data-index="2" data-category="svg" rel="noopener noreferrer" class="data-app-button">
<span class="app-name">SVGOMG</span>
</a>
The JavaScript:
// clicks
document.addEventListener(
"click",
function (event) {
if (event.target.closest("[data-app-index]")) {
let appID = event.target.closest("[data-app-index]").getAttribute("data-app-index");
return openApp(appID);
}
},
false
);
// openApp() - a regular window.open()
const openApp = appID => {
event.preventDefault();
// get app options
let appOptions = State.appList[appID];
appOptions.window = appOptions.window || {};
// merge with defaults
let defaultWindow = State.getDefaultWindow;
let options = Object.assign({}, defaultWindow, appOptions.window);
// center window
options.left = screen.width / 2 - options.width / 2;
options.top = screen.height / 2 - options.height / 2;
// translate to window.open args
let args = [];
for (let [key, val] of Object.entries(options)) args.push(`${key}=${val}`);
args = args.join(",");
// open app
return window.open(appOptions.url, appOptions.name, args);
};
Before updating to Chrome 73 everything worked as expected: the window.open() function displayed the web page correctly.
Now the window opens but nothing is visible.
OK I think I found the solution: I noticed that right after the window was open, the parent window (PWA) got the focus back. So it behaved like a pop-under. As pop-under are a really bad/malicious practice I think Chrome is blocking the child popup window.
So I removed the return statement on the event callback:
// I changed this:
return openApp(appID);
// to this:
openApp(appID);
It seems that the parent window do not steal the focus anymore and the popup content is displayed correctly.
I have an HTML5 canvas based Javascript component that needs to capture and release mouse events. In the control the user clicks an area inside it and drags to affect a change. On PC I would like the user to be able to continue dragging outside of the browser and for the canvas to receive the mouse up event if the button is released outside of the window.
However, according to my reading setCapture and releaseCapture aren't supported on Chrome.
Is there a workaround?
An article written in 2009 details how you can implement cross-browser dragging which will continue to fire mousemove events even if the user's cursor leaves the window.
http://news.qooxdoo.org/mouse-capturing
Here's the essential code from the article:
function draggable(element) {
var dragging = null;
addListener(element, "mousedown", function(e) {
var e = window.event || e;
dragging = {
mouseX: e.clientX,
mouseY: e.clientY,
startX: parseInt(element.style.left),
startY: parseInt(element.style.top)
};
if (element.setCapture) element.setCapture();
});
addListener(element, "losecapture", function() {
dragging = null;
});
addListener(document, "mouseup", function() {
dragging = null;
}, true);
var dragTarget = element.setCapture ? element : document;
addListener(dragTarget, "mousemove", function(e) {
if (!dragging) return;
var e = window.event || e;
var top = dragging.startY + (e.clientY - dragging.mouseY);
var left = dragging.startX + (e.clientX - dragging.mouseX);
element.style.top = (Math.max(0, top)) + "px";
element.style.left = (Math.max(0, left)) + "px";
}, true);
};
draggable(document.getElementById("drag"));
The article contains a pretty good explanation of what's going on, but there are a few gaps where knowledge is assumed. Basically (I think), in Chrome and Safari, if you handle mousemove on the document then, if the user clicks down and holds the mouse, the document will continue receiving mousemove events even if the cursor leaves the window. These events will not propagate to child nodes of the document, so you have to handle it at the document level.
Chrome supports setPointerCapture, which is part of the W3C Pointer events recommendation. Thus an alternative would be to use pointer events and these methods.
You might want to use the jquery Pointer Events Polyfill to support other browsers.
The other day I stumbled upon with this example of a Javascript audio recorder:
http://webaudiodemos.appspot.com/AudioRecorder/index.html
Which I ended up using for implementing my own. The problem I'm having is that in this file:
var audioContext = new webkitAudioContext();
var audioInput = null,
realAudioInput = null,
inputPoint = null,
audioRecorder = null;
var rafID = null;
var analyserContext = null;
var canvasWidth, canvasHeight;
var recIndex = 0;
/* TODO:
- offer mono option
- "Monitor input" switch
*/
function saveAudio() {
audioRecorder.exportWAV( doneEncoding );
}
function drawWave( buffers ) {
var canvas = document.getElementById( "wavedisplay" );
drawBuffer( canvas.width, canvas.height, canvas.getContext('2d'), buffers[0] );
}
function doneEncoding( blob ) {
Recorder.forceDownload( blob, "myRecording" + ((recIndex<10)?"0":"") + recIndex + ".wav" );
recIndex++;
}
function toggleRecording( e ) {
if (e.classList.contains("recording")) {
// stop recording
audioRecorder.stop();
e.classList.remove("recording");
audioRecorder.getBuffers( drawWave );
} else {
// start recording
if (!audioRecorder)
return;
e.classList.add("recording");
audioRecorder.clear();
audioRecorder.record();
}
}
// this is a helper function to force mono for some interfaces that return a stereo channel for a mono source.
// it's not currently used, but probably will be in the future.
function convertToMono( input ) {
var splitter = audioContext.createChannelSplitter(2);
var merger = audioContext.createChannelMerger(2);
input.connect( splitter );
splitter.connect( merger, 0, 0 );
splitter.connect( merger, 0, 1 );
return merger;
}
function toggleMono() {
if (audioInput != realAudioInput) {
audioInput.disconnect();
realAudioInput.disconnect();
audioInput = realAudioInput;
} else {
realAudioInput.disconnect();
audioInput = convertToMono( realAudioInput );
}
audioInput.connect(inputPoint);
}
function cancelAnalyserUpdates() {
window.webkitCancelAnimationFrame( rafID );
rafID = null;
}
function updateAnalysers(time) {
if (!analyserContext) {
var canvas = document.getElementById("analyser");
canvasWidth = canvas.width;
canvasHeight = canvas.height;
analyserContext = canvas.getContext('2d');
}
// analyzer draw code here
{
var SPACING = 3;
var BAR_WIDTH = 1;
var numBars = Math.round(canvasWidth / SPACING);
var freqByteData = new Uint8Array(analyserNode.frequencyBinCount);
analyserNode.getByteFrequencyData(freqByteData);
analyserContext.clearRect(0, 0, canvasWidth, canvasHeight);
analyserContext.fillStyle = '#F6D565';
analyserContext.lineCap = 'round';
var multiplier = analyserNode.frequencyBinCount / numBars;
// Draw rectangle for each frequency bin.
for (var i = 0; i < numBars; ++i) {
var magnitude = 0;
var offset = Math.floor( i * multiplier );
// gotta sum/average the block, or we miss narrow-bandwidth spikes
for (var j = 0; j< multiplier; j++)
magnitude += freqByteData[offset + j];
magnitude = magnitude / multiplier;
var magnitude2 = freqByteData[i * multiplier];
analyserContext.fillStyle = "hsl( " + Math.round((i*360)/numBars) + ", 100%, 50%)";
analyserContext.fillRect(i * SPACING, canvasHeight, BAR_WIDTH, -magnitude);
}
}
rafID = window.webkitRequestAnimationFrame( updateAnalysers );
}
function gotStream(stream) {
// "inputPoint" is the node to connect your output recording to.
inputPoint = audioContext.createGainNode();
// Create an AudioNode from the stream.
realAudioInput = audioContext.createMediaStreamSource(stream);
audioInput = realAudioInput;
audioInput.connect(inputPoint);
// audioInput = convertToMono( input );
analyserNode = audioContext.createAnalyser();
analyserNode.fftSize = 2048;
inputPoint.connect( analyserNode );
audioRecorder = new Recorder( inputPoint );
zeroGain = audioContext.createGainNode();
zeroGain.gain.value = 0.0;
inputPoint.connect( zeroGain );
zeroGain.connect( audioContext.destination );
updateAnalysers();
}
function initAudio() {
if (!navigator.webkitGetUserMedia)
return(alert("Error: getUserMedia not supported!"));
navigator.webkitGetUserMedia({audio:true}, gotStream, function(e) {
alert('Error getting audio');
console.log(e);
});
}
window.addEventListener('load', initAudio );
As you might be able to see, the initAudio() function (the one wich ask the user for permission to use his/her microphone) is called inmediately when the page is loaded (read the last line) with this method:
window.addEventListener('load', initAudio );
Now, I have this code in the HTML:
<script type="text/javascript" >
$(function() {
$("#recbutton").on("click", function() {
$("#entrance").hide();
$("#live").fadeIn("slow");
toggleRecording(this);
$(this).toggle();
return $("#stopbutton").toggle();
});
return $("#stopbutton").on("click", function() {
audioRecorder.stop();
$(this).toggle();
$("#recbutton").toggle();
$("#live").hide();
return $("#entrance").fadeIn("slow");
});
});
</script>
And as you can see, I call the toggleRecording(this) function (the one wich starts the recording process) only after the #recbutton is pressed. Now, everything works fine with this code BUT, the user gets prompted for microphone permission as soon as the page is loaded and I want to ask them for permission to use the microphone ONLY AFTER they clicked the #recbutton Do you understand me? I tought that if I remove the last line of the first file:
window.addEventListener('load', initAudio );
and modify my embedded script like this:
<script type="text/javascript" >
$(function() {
$("#recbutton").on("click", function() {
$("#entrance").hide();
$("#live").fadeIn("slow");
initAudio();
toggleRecording(this);
$(this).toggle();
return $("#stopbutton").toggle();
});
return $("#stopbutton").on("click", function() {
audioRecorder.stop();
$(this).toggle();
$("#recbutton").toggle();
$("#live").hide();
return $("#entrance").fadeIn("slow");
});
});
</script>
I might be able to achieve what I wanted, and actually I am, the user doesn't get prompted for his/her microphone until they click the #recbutton. The problem is, the audio never get's recorded, when you try to download it, the resulting WAV it is empty.
How can I fix this?
My project's code is at: https://github.com/Jmlevick/html-recorder
No, your problem is that getUserMedia() has an asynchronous callback (gotMedia()); you need to have the rest of your code logic in the startbutton call (the toggleRecording bit, in particular) inside that callback, because right now it's getting executed before getUserMedia returns (and sets up the audio nodes).
I found an elegant & easy solution for this (or at least I see it that way):
What I did was toss "main.js" and "recorder.js" inside a getScript call that is executed only when a certain button (#button1) is clicked by the user... These scripts do not get loaded with the webpage itself until the button it's pressed, but we need some more nifty tricks to make it work the way I described and wanted above:
in main.js, I changed:
window.addEventListener('load', initAudio );
for:
window.addEventListener('click', initAudio );
So when the scripts are loaded into the page with getScript the "main.js" file now listens for a click event in the webpage to ask the user for the microphone. Next, I had to create a hidden button (#button2) on the page wich is fakely clicked by jQuery exactly right after the scripts are loaded on the page, so it triggers the "ask for microphone permisson" event and then, just below that line of code wich generates the fake click I added:
window.removeEventListener("click", initAudio, false);
so the "workflow" for this trick ends up as follows:
User presses a button wich loads the necesary js files into the page with getScript, it's worth mentioning that now the "main.js" file listens for a click event on the window instead of a load one.
We have a hidden button wich is "fakely clicked" by jQuery just in the moment you click the first one, so it triggers the permisson event for the user.
Once this event is triggered, the click event listener is removed from the window, so it never fires the "ask for permisson" event again when the user clicks anywhere on the page.
And basically that's all folks! :) now when the user goes into the page he/she never get asked for microphone permisson until they click a "Rec" button on the page just as I wanted. With one click of the user we do 3 things in jQuery, but for the user it seems like nothing happened other that the "microphone permisson message" appearing on the screen instantly right after they click the "Rec" Button.