Using Chrome, Tampermonkey and jQuery - I would like to discover and remove key event bindings on a third party website.
I have tried to bind (over them) and prevent propagation using:
,_cancel:function(e){
//console.log('keyProcessed');
e.cancelBubble = true; // IE4+
try {
e.keyCode = 0;
} catch (e) {
} // IE5
if (window.event) {e.returnValue = false; } // IE6
if (e.preventDefault) {e.preventDefault(); } // moz/opera/konqueror
if (e.stopPropagation) {e.stopPropagation(); } // all
return false;
}
... but the key still fires.
I have tried enumerating elements with jQuery:
$('*').each(function(){
if(this.onkeydown){
console.log(this.tagName,this.onkeydown);
}
});
... which gives no results for keydown keyup or keypress
I have also tried
$('*').unbind();
How can I enumerate and kill bindings?
Edit, Updated
Before click at button , default action at input element is to input text ; following click at button , evt.preventDefault() called , text prevented from being input at element.
Try
var events = ["keydown", "keyup", "keypress"];
$.each($("*"), function (index, elem) {
if ($._data(elem, "events") != undefined) {
// check `elem` for `keydown`, `keyup`, `keypress` `events`
if (new RegExp(events.join("|")).test(
Object.keys($._data(elem, "events")))) {
$.each(events, function (key, val) {
// do stuff at `value` having `events`
// call `evt.preventDefault` at `elem`
// having `keydown`, `keyup`, `keypress` `events` `events`
$(elem).on(val, function(evt) {
evt.preventDefault()
})
})
}
};
});
$(function () {
$("input:eq(0)").on("keyup", function (e) {
console.log(e);
});
$("input:eq(1)").on("keydown", function (e) {
console.log(e);
});
$("input:eq(2)").on("keypress", function (e) {
console.log(e);
});
$("button").on("click", function() {
var events = ["keydown", "keyup", "keypress"];
$.each($("*"), function (index, elem) {
if ($._data(elem, "events") != undefined) {
if (new RegExp(events.join("|")).test(
Object.keys($._data(elem, "events")))) {
$.each(events, function (key, val) {
$(elem).on(val, function(evt) {
evt.preventDefault()
})
})
}
};
});
})
})
input:nth-of-type(1) {
width : 50px;
height : 50px;
background : blue;
}
input:nth-of-type(2) {
display : block;
width : 50px;
height : 50px;
background : yellow;
}
input:nth-of-type(3) {
display : block;
width : 50px;
height : 50px;
background : orange;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button>click to remove keydown, keyup, keypress events below</button><br />
<input type="text" />
<input type="text" />
<input type="text" />
Related
I've got an issue while I'm trying to combine touchstart and mousedown in 1 function. I've used an a tag as the target element of the function for going to the link directly when I touched or clicked the tag.
The issue is when I touch the middle of a tag, link doesn't respond. it only works when I click the element or touch the edge of the a tag, and the output fires mousedown.
In the mobile mode, try to click the edge of a tag as much as you would possible like a grey dot in the picture above. I've created an CodePen example for looking, testing and understanding better.
How would I fix this issue?
class Slider {
constructor($el, paragraph) {
this.$el = $el;
this.paragraph = paragraph;
}
start(e) {
e.preventDefault();
var type = e.type;
if (type === 'touchstart' || type === 'mousedown') this.paragraph.text(this.paragraph.text() + ' ' + type);
return false;
}
apply() {
this.$el.bind('touchstart mousedown', (e) => this.start(e));
}
}
const setSlider = new Slider($('#anchor'), $('.textbox'), {passive: false});
setSlider.apply();
a {
display: block;
width: 100px;
height: 100px;
background-color: orange;
}
<a id="anchor" href="https://google.co.uk">Tap or Click Me</a>
<p class="textbox"></p>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
========= Progress Update ==========
I've just added move & end function then I have to click twice for moving on to the linked website. It keeps getting worse and have no idea how to solve this issue.
class Slider {
constructor($el, paragraph) {
this.$el = $el;
this.paragraph = paragraph;
}
start(e) {
e.preventDefault();
var type = e.type;
if (type === 'touchstart' || type === 'mousedown') this.paragraph.text(this.paragraph.text() + ' ' + type);
this.$el.bind('touchmove mousemove', (e) => this.move(e));
this.$el.bind('touchend mouseup', (e) => this.end(e));
return false;
}
move(e) {
var type = e.type;
if (type === 'touchstart' || type === 'mousedown') this.paragraph.text(this.paragraph.text() + ' ' + type);
return false;
}
end(e) {
console.log('test');
this.$el.on('click');
this.$el.off('touchstart touchend');
return false;
}
apply() {
this.$el.bind('touchstart || mousedown', (e) => this.start(e));
}
}
const setSlider = new Slider($('#anchor'), $('.textbox'));
setSlider.apply();
======== Progress Updated After Bounty (Latest) ========
After dozens of tried, I've finally figured out and solve the previous problem but I've faced up a new issue that can't draggable and redirecting instantly.
When I use the preventDefault in the start function, all of the events work fine. The only issue of this case is dragging doesn't prevent redirecting link from the a tag. It always send me to the website no matter which ways to call the functions, clicked or dragged.
when I don't use the preventDefault, dragging doesn't work. it only works clicking the elements.
My final goal is to prevent redirecting link of the a tag from the both events, touchmove and mousemove. I've been searched about on google so many times but haven't got any of the clues.
I've written an example in Codepen and this is what I've done so far:
class Slider {
constructor($el, paragraph) {
this.$el = $el;
this.paragraph = paragraph;
}
start(e) {
var type = e.type;
if (type === 'touchstart') {
this.paragraph.text(this.paragraph.text() + ' ' + type);
} else if (type === 'mousedown') {
this.paragraph.text(this.paragraph.text() + ' ' + type);
}
}
move(e) {
var type = e.type;
}
end(e) {
var type = e.type;
if (type === 'touchend') {
console.log('touchstart enabled');
} else if (type === 'mouseup') {
console.log('mousedown enabled');
}
}
apply() {
this.$el.bind({
touchstart: (e) => this.start(e),
touchmove: (e) => this.move(e),
touchend: (e) => this.end(e),
mousedown:(e) => this.start(e),
onmousemove: (e) => this.move(e),
mouseup: (e) => this.end(e)
});
}
}
const setSlider = new Slider($('#anchor'), $('.textbox'));
setSlider.apply();
a {
display: block;
width: 100px;
height: 100px;
background-color: orange;
}
<a id="anchor" href="https://google.co.uk">Tap or Click Me</a>
<p class="textbox"></p>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
I think I found the solution about this. I add this code for some people in the future who are trying hard to search the same problem such as me.
function start(event, etype, condition) {
console.log(etype); // track the type of event
if (!condition) event.preventDefault(); // compare etype(eventType). Set preventDefault if condition is falsy.
items.off('click');
items.on({
['touchmove mousemove']: (event) => move(event, etype, condition),
['touchend mouseup']: end
});
}
function move(event, etype, cnd) {
if (cnd) event.preventDefault();
console.log(cnd); // track the type of event from the condition
items.on('click', function(event) {event.preventDefault();});
}
function end(event) {
items.off('touchmove mousemove touchend mouseup');
}
var items = $('.item a');
items.on('touchstart mousedown', function() {
var eventType = event.type;
var condition = (eventType === 'touchstart' || eventType === 'mousedown');
start(event, eventType, condition);
});
#anchor {
display: block;
width: 100px;
height: 100px;
background-color: orange;
}
.item {
background-color: gray;
}
.item + .item {
margin-top: 10px;
}
.item a {
cursor: pointer;
display: inline-block;
background-color: blue;
color: white;
padding: 9px;
width: 100%;
height: 100%;
}
<div id="items" class="items">
<div class="item">
<a target="_blank" href="https://google.com">Anchor</a>
</div>
<div class="item">
<a target="_blank" href="https://google.com">Anchor</a>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
i think i figured out little solution for you. enable the preventDefault and afterwards enable the draggable for the a tag. Let me know if this works for you.
start(e) {
e.preventDefault();
...//rest of the code
apply() {
$el.draggable("enable");
...//rest of the code
I'm using this code to switch background when hovering an element and pressing the shift key. It works if I hold the shift key down before I enter the element but not if I'm already on the element and pressing the shift key. Ideas? Thanks!
var shiftPressed = null;
$(document).on({
keydown: function (e) {
if( e.shiftKey )
{
shiftPressed = true;
}
},
keyup: function (e) {
shiftPressed = false;
}
});
$('div').on({
mousemove: function (e) {
if( shiftPressed )
{
$(this).css('backgroundColor', 'red');
}
else
{
$(this).css('backgroundColor', '');
}
},
mouseover: function (e) {
if( shiftPressed )
{
$(this).css('backgroundColor', 'red');
}
else
{
$(this).css('backgroundColor', '');
}
},
mouseleave: function (e) {
$(this).css('backgroundColor', '');
}
}, 'span');
div {
position:absolut;
width:100px;
height:100px;
background:#999;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<span>TARGET</span>
</div>
A div cannot be focused (by default); thus, it cannot capture key presses.
However, if you bind the keypress event listener to the document, the event will fire on every key press. Keep the mouseover and mouseleave bound to the div, because they can be fired with a non-focusable element.
Also, a keyboard event is different than a mouse event, so you can't access shiftKey of e in mouseover. Instead, I would use the && operator to test if both conditions are met: the shift key and the mouse in, storing whether the shift key is pressed in a boolean and checking that on mouseover / mouseleave.
var shiftPressed = null; // global data
$(document).on('keyup keydown', function(e) {
shiftPressed = e.shiftKey;
updateDivs();
});
$('div').on({
mouseover: function(e) {
$(this).data('hovered', true); // element-specific data
updateDiv(this);
},
mouseleave: function(e) {
$(this).data('hovered', false); // element-specific data
updateDiv(this);
}
});
function updateDiv(div) {
if (shiftPressed && $(div).data('hovered'))
$(div).css('backgroundColor', 'red');
else
$(div).css('backgroundColor', '');
}
/** updates all divs in document */
function updateDivs() {
$('div').each(function() {
updateDiv(this);
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>Hello world</div>
<div>Hi world</div>
You can add a bunch of code to listen to the mouse position and also an event for the keydown and figure out where the cursor is...
There might be a better solution, but this works.
$('div').on("mouseenter", "span", function(e) {
if (e.shiftKey) {
$(this).css('backgroundColor', 'red');
}
}).on("mouseleave", "span", function(e) {
$(this).css('backgroundColor', 'white');
})
let x = 0;
let y = 0;
$(document).on("mousemove", function(e) {
x = e.clientX;
y = e.clientY;
}).on("keydown keyup", function(e) {
$(document.elementFromPoint(x, y)).trigger({
type: e.type==="keydown" ? "mouseenter" : "mouseleave",
shiftKey: true
})
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<span>Test</span>
<span>Test</span>
<span>Test</span>
<span>Test</span>
<span>Test</span>
<span>Test</span>
<span>Test</span>
<span>Test</span>
<span>Test</span>
</div>
Other way is to set an active element and not worry about the x and y position.
let active;
$('div').on("mouseenter", "span", function(e) {
active = this;
if (e.shiftKey) {
$(this).css('backgroundColor', 'red');
}
}).on("mouseleave", "span", function(e) {
console.log(e)
$(this).css('backgroundColor', 'white');
if (active && !e.custTrigger && $(this).is(active)) active = null
})
$(document).on("keydown keyup", function(e) {
if (active) {
$(active).trigger({
type: e.type==="keydown" ?"mouseenter" : "mouseleave",
shiftKey: true,
custTrigger: true
})
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<span>Test</span>
<span>Test</span>
<span>Test</span>
<span>Test</span>
<span>Test</span>
<span>Test</span>
<span>Test</span>
<span>Test</span>
<span>Test</span>
</div>
This is a bit sloppy but it should work. Just set some vars and then check them whenever an event is fired.
N.B. you have to click once inside the iframe to make it work (so that it can capture keypresses). Presumably on your page you're not using an iframe, so that won't be necessary.
Array.prototype.forEach.call(document.querySelectorAll('.box'), box => {
let hover = false;
let shift = false;
box.addEventListener('mouseenter', ev => {
hover = true;
check();
});
box.addEventListener('mouseleave', ev => {
hover = false;
check();
});
document.addEventListener('keydown', ev => {
shift = ev.shiftKey;
check();
});
document.addEventListener('keyup', ev => {
shift = ev.shiftKey;
check();
});
function check() {
box.style.backgroundColor = shift && hover ? 'red' : '';
}
});
div {
width: 100px;
height: 100px;
background: #999;
margin: 5px;
display: inline-block;
}
<div class="box">
<span>TARGET</span>
</div>
<div class="box">
<span>TARGET</span>
</div>
<div class="box">
<span>TARGET</span>
</div>
A little late to the party, but here's a non jquery example that monitors keystrokes and adds the various shift/ctrl/alt states to the html classList in real time.
css can respond accordingly.
note
to test this code in the stackoverflow snippet, focus is required because the snippet runs in an iframe.
when running as a top level page, the current document is focused by default.
first click "Run Code Snippet" and the select the iframe directly below it - click on the word "shift", and you will be focused in the iframe
you can then tap the shift key.
const html = document.querySelector("html");
["keyup","keydown"].forEach(function(ev){
document.addEventListener(ev,updateHtmlKeyClasses);
});
function updateHtmlKeyClasses(e){
["shiftKey","ctrlKey","altKey"].forEach(function(shiftKey){
html.classList[e[shiftKey]?"add":"remove"](shiftKey);
});
}
div {
margin :10px;
}
html.shiftKey .shiftstate,
html.ctrlKey .ctrlstate,
html.altKey .altstate {
background:lime;
}
.test {
background:cyan;
}
.test:hover {
background:yellow;
}
html.shiftKey .test:hover {
background:red;
}
<div class="shiftstate">
shift
</div>
<div class="ctrlstate">
ctrl
</div>
<div class="altstate">
alt
</div>
<div class="test">
TEST AREA
</div>
keypress doesn't capture the shift-key
This [the keypress event] is similar to the keydown event, except that modifier and non-printing keys such as Shift, Esc, and delete trigger keydown events but not keypress events.
I have a function that uses the value of a textbox (prodinput) to hide/show links in a dropdown list. It works when a user types in a string manually but when I want to auto-populate the value by passing a url parameter I'll need to trigger a keyup or keydown to get it to call the function.
Here is the function that does the search (located in the core.js):
prodinput.on('keyup, keydown',function() {
var search = $(this).val().toLowerCase();
$('.support-product .browse-products a').each(function() {
if($(this).text().toLowerCase().search(search) > -1) {
$(this).parent().show();
} else {
$(this).parent().hide();
}
});
});
Here is the function I'm using to trigger the function above (located on the page I'm trying to run it on.
$(function(){
$target = $('.browse-products .display');
$target.val($trimmed);
$('.browse-products').addClass('active');
$target.focus();
var e = jQuery.Event( "keydown" );
$target.trigger(e);
});
I've tried using:
$target.keyup();
and as shown above:
var e = jQuery.Event( "keydown" );
$target.trigger(e);
I'm wondering if it's a problem with the order in which things load on the page.
I'd put your keyup code in a named function.
$(function () {
myFunction();
prodinput.on('keyup, keydown', function () {
myFunction();
})
};
var myFunction = function () {
var search = $('#prodinput').val().toLowerCase();
$('.support-product .browse-products a').each(function () {
if ($(this).text().toLowerCase().search(search) > -1) {
$(this).parent().show();
} else {
$(this).parent().hide();
}
});
};
Assuming you don't need to support ancient browsers you can just listen for the input event which covers keypress and change events. Then after attaching the listener simply trigger the event:
$(function() {
$("#prodinput").on('input', function() {//alternatively you could use change and keyup
var search = $(this).val().toLowerCase();
$('.support-product .browse-products a').each(function() {
if ($(this).text().toLowerCase().search(search) > -1) {
$(this).parent().show();
} else {
$(this).parent().hide();
}
});
}).trigger("input");//trigger the event now
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="search" id="prodinput" value="peanuts" />
<div class="support-product">
<ul class="browse-products">
<li>jam</li>
<li>elephants</li>
<li>peanuts</li>
</ul>
</div>
I would like to change text to input text by clicking on it :
Currently I've:
<div class="wrapper">
<span class="text-content">Double Click On Me!</span>
</div>
And in javascript:
//plugin to make any element text editable
$.fn.extend({
editable: function () {
$(this).each(function () {
var $el = $(this),
$edittextbox = $('<input type="text"></input>').css('min-width', $el.width()),
submitChanges = function () {
if ($edittextbox.val() !== '') {
$el.html($edittextbox.val());
$el.show();
$el.trigger('editsubmit', [$el.html()]);
$(document).unbind('click', submitChanges);
$edittextbox.detach();
}
},
tempVal;
$edittextbox.click(function (event) {
event.stopPropagation();
});
$el.dblclick(function (e) {
tempVal = $el.html();
$edittextbox.val(tempVal).insertBefore(this)
.bind('keypress', function (e) {
var code = (e.keyCode ? e.keyCode : e.which);
if (code == 13) {
submitChanges();
}
}).select();
$el.hide();
$(document).click(submitChanges);
});
});
return this;
}
});
//implement plugin
$('.text-content').editable().on('editsubmit', function (event, val) {
console.log('text changed to ' + val);
});
But I don't know how to change double click on simple click ! I've tried to replace $el.dblclick(...) by $el.click(), but it doesn't work.
Is anybody have a solution ?
When you just change $el.dblclick to $el.click it will also handled with $(document).click(submitChanges); event. So $el.click handler should return false to stop further event processing.
$el.click(function (e) { // <---------- click
tempVal = $el.html();
$edittextbox.val(tempVal).insertBefore(this).bind('keypress', function (e) {
var code = (e.keyCode ? e.keyCode : e.which);
if (code == 13) {
submitChanges();
}
}).select();
$el.hide();
$(document).click(submitChanges);
return false; // <------------------------ stop further event handling
});
http://jsfiddle.net/zvm8a7cr/
You may use the contenteditable attribute of html
var initialContent = $('.text-content').html();
$('.text-content')
.on('blur', function(){
if(initialContent != $(this).html())
alert($(this).text());
if($(this).html() == ''){
$(this).html(initialContent);
}
})
.on('click', function(){
if(initialContent == $(this).html()) {
$(this).html('');
}
});;
.text-content {
border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="wrapper">
<span contenteditable="true" class="text-content">Click On Me!</span>
</div>
This is my HTML structure.
<div id="dvHirearachy" class="MarginTB10">
<span>
<label>Hierarchy Names</label><br />
<input type="text" name="txtHierarchy" />
<a id="ancRemove" href="#">Remove</a>
</span><br />
<a id="ancNew" href="#">New Hierarchy Name</a>
</div>
On click of anchor tag "ancNew" , I am generating again the complete span tag above mentioned in the markup.
The problem is on click of textbox also the span structure is getting generated. Same problem i was facing on click of "ancRemove" for that i tried to stop the event bubbling, it has worked for this but not for the textbox.
my script.
$(document).ready(function () {
$("#ancRemove").click(function (e) {
RemoveHierarchy(this, e);
});
$("#ancNew").click(function (e) {
generateNewHierarchy(e);
});
});
function generateNewHierarchy(e) {
if (window.event) {
var e = window.event;
e.cancelBubble = true;
} else {
e.preventDefault();
e.stopPropagation();
var container = createElements('span', null);
$(container).append(createElements('input', 'text'));
$(container).append(createElements('a', null));
$(container).append("<br/>").prependTo("#ancNew");
$(container).children('input[type=text]').focus();
}
}
function createElements(elem,type) {
var newElem = document.createElement(elem);
if (type != null) {
newElem.type = "input";
newElem.name = "txtHierarchy";
$(newElem).addClass('width_medium');
}
if (elem == "a") {
newElem.innerHTML = "Remove";
$(newElem).click(function (e) {
RemoveHierarchy(this,e);
});
}
return newElem;
}
function RemoveHierarchy(crntElem, e) {
e.stopPropagation();
$(crntElem).parents("span:first").remove();
}
what is the way to avoid the situation.
Check this jsfiddle :
http://jsfiddle.net/xemhK/
Issue is prepandTo statement, It is prepading the elements in #ancNew anchor tag, thats why all textbox and remove anchor, are propagating click event of #ancNew, and it is calling generateNewHierarchy() function.
Change in $(container).append("<br/>").prepandTo("#ancNew"); to $(container).append("<br/>").insertBefore("#ancNew");
function generateNewHierarchy(e) {
if (window.event) {
var e = window.event;
e.cancelBubble = true;
} else {
e.preventDefault();
e.stopPropagation();
var container = createElements('span', null);
$(container).append(createElements('input', 'text'));
$(container).append(createElements('a', null));
//$(container).append("<br/>").prepandTo("#ancNew");
$(container).append("<br/>").insertBefore("#ancNew");
$(container).children('input[type=text]').focus();
}
}
and in createElements
if (elem == "a") {
newElem.innerHTML = "Remove";
$(newElem).attr("href","#").click(function (e) {
RemoveHierarchy(this,e);
});
}