The following script is aimed to run on facebook.com's conversations page (the page in which a user can see all its conversations).
The script's purpose is to automize the "delete conversation" process which naturally includes 4 clicks and can be tiresome and time wasting when you have hundreds of conversations --- deletion will be done from keyboard by hitting the "D" key.
I run the script with Greasemonkey.
The script is comprised of 4 main segments:
- Listen to all "D" key hitting events.
- Click the link that opens the modal with the "Delete" option (the small chainwheel).
- Clink the "Delete" link in that modal (it will open a second modal "delete confirmation").
- Click the new "Delete" link in that modal, to confirm conversation deletion.
My script
document.addEventListener('keydown', (k)=>{
if ( k.keyCode === 68 ) {
console.log('keydown: D');
return dC();
}
});
let dC = ()=>{
document.querySelector('._5blh._4-0h').click();
document.querySelector('.uiContextualLayer > [id^="js"] > div > ul > li:nth-child(4)').click();
setTimeout(()=>{ document.querySelector('._3quh._30yy._2t_._3ay_._5ixy').click(); }, 500);
};
As a beginner, I tried put parts of the code in functions, I tried iterating with for
instead forEach()
, I tried using return dC()
or return false
under the dC()
call. All of these yielded the same results so I walked in circles not understanding (or denying) a deeper logical error which I sorely miss.
Reproducing
Install as Greasemonkey script, (match as https:www.facebook.com/*
just for the test), go to conversations page and hit "D".
My question
Why the event is listened only once? That is, why clicking D once will case the script to work but any further clicks will do nothing?
I will have to refresh the page for that to reuse the script and that's not the intended behavior.
Note: I would prefer a vanilla solution.
As written, function dC(), it does 4 actions:
The problem was in action #3:
Seemingly, every click on 'settings' menu (action #2), a new 'settings-menu' instance is created (new object added to the DOM), thus this line won't work:
document.querySelector('.uiContextualLayer > [id^="js"] > div > ul > li:nth-child(4)').click();
On the one hand, querySelector returns the first element that answers the given pattern.
On the other hand, Facebook creates a new '.uiContextualLayer' each time the settings button is clicked (target the menu chainwheel link and stretch up your devtool window to note the new element added).
Hence, what we do is to check all chainwheel elements after and then work with the newest (last) element each time:
let menu = document.querySelectorAll('.uiContextualLayer._5v-0._53il');
menu = menu[menu.length-1];
Here is the final code.
(I added few more timeouts to make sure drawing the UI is finished)
let dC = ()=>
{
// clicking the 'settings'
document.querySelector('._5blh._4-0h').click();
setTimeout(() => {
// taking the last instance of 'menu popup':
let menu = document.querySelectorAll('.uiContextualLayer._5v-0._53il');
menu = menu[menu.length-1];
// finding 'delete' button inside the menu popup
let lis = menu.querySelectorAll('ul > li');
let target = null;
for (let i=0;!target && i<lis.length;++i)
{
let span = lis[i].querySelector('a span span');
if (!span) continue;
if (span.innerHTML.contains('Delete'))
target = lis[i];
}
if (!target) {console.log('cannot find delete btn'); return;}
// clicking 'delete' button in menu
setTimeout(() => {
target.click();
setTimeout(()=>{
// clicking delete in modal
document.querySelector('._3quh._30yy._2t_._3ay_._5ixy').click();
}, 500);
}, 10);
},10);
};
Push the key values into arrays and unset these arrays after the event was over.
// Create two empty arrays
var map = [];
var down = [];
$(document).on('keydown', 'body', function(e) {
// Check whether the map having any key values
if (!map[e.which]) {
// Set the keydown value to down array
down.push(e.which);
if (down[0] === 68) {
// Events to be done
}
}
map[e.which] = true;
// Once the key-down pressed and event done ,the keyup unset the array while key up
}).keyup(function(e) {
map[e.which] = false;
// unset(down,e.which);
down = [];
e.which = [];
});