Disable focus event on left click only in AngularJS - javascript

I have an angular-strap datepicker that I only want to show on right click.
This I want to do over the default focus() method, as this is convenient for closing the thing once it blurs. To use the focus method on any element like a DIV, I added a tabindex.
The problem is, I can't seem to be able to disable the focus on the left click only. It's either disabled completely, or working for both.
I've already prevented the context menu to show on right click.
directives.directive('ngRightClick', ["$parse", function($parse) {
return function(scope, element, attrs) {
var fn = $parse(attrs.ngRightClick);
element.bind('contextmenu', function(event) {
scope.$apply(function() {
event.preventDefault();
fn(scope, {$event:event});
});
});
};
}])
For the left click, I've tried preventDefault(), but that doesn't really work. Then I've tried to blur it on click, which works, but the focus event is still called beforehand, for a fraction of a second.
directives.directive('ngLeftNofocus', ["$parse", function($parse) {
return function(scope, element, attrs) {
var fn = $parse(attrs.ngLeftNofocus);
element.bind('click', function(event) {
scope.$apply(function() {
event.preventDefault();
element[0].blur();
});
});
};
}])
This can all be found in a Plnkr: http://plnkr.co/edit/T2YBYwfqSLKxCUL0ITgk?p=preview
I know there are some solutions to prevent the focus using jQuery on this site, but I need it to stay active for the right click somehow.
Alternatively, if someone has a better way to trigger the datepicker on right click only (that it also closes once the user clicks anywhere else), I'd love to look at that too.

You could use bs-show attribute to toggle the datepicker manually.
Combined with the ng-right-click and ng-blur, it will give what you want.
<div bs-datepicker ng-model="test" tabindex="1"
data-trigger="manual"
bs-show="showDatePicker"
ng-blur="showDatePicker = false"
ng-right-click="showDatePicker = true">CLICK HERE</div>
Example Plunker: http://plnkr.co/edit/zJXdZOIZ9XQey3BkQ3I2?p=preview

Related

Link on dropdown Selectize.js

I'm trying to put a link on a selectize dropdown in order to allow the user make an operation other than select an item while still allowing that the user selects the item as main option.
Here is an example of what I want to achieve (but is not working as expected):
What I did is plainly insert links on the HTML. But it's not working, I suppose that for some kind of event propagation stop, is it possible to achieve with selectize?
Nobody did answer yet and I think there's more to say about, so, here is an example of what I did:
render: {
option: function(item) {
return '<div><span>'+item.label+'</span>'
+ '<div class="pull-right">'
+ 'Link'
+ '</div></div>';
}
}
As you can see, I did change the "option" renderization, and inserted a link in plain HTML. The problem is that -as shown on image- when I do click the link, the browser does not follow the link, but executes the default action for selectize, which is selecting the clicked element.
What I want to achieve is to make it follow the link when clicked.
Here is a fiddle of what I did: http://jsfiddle.net/uetpjpa9
The root problem is that Selectize has mousedown and blur handlers that are dismissing the dropdown before the mouseup event that would complete the click that your link is waiting for from ever occurring. Avoiding this without direct support from Selectize is not easy, but it is possible thanks to its plugin system and the amount of access it gives you to Selectize internals.
Here's a plugin that allows a dropdown element with the class clickable to be clicked on. (demo)
Selectize.define('option_click', function(options) {
var self = this;
var setup = self.setup;
this.setup = function() {
setup.apply(self, arguments);
var clicking = false;
// Detect click on a .clickable
self.$dropdown_content.on('mousedown click', function(e) {
if ($(e.target).hasClass('clickable')) {
if (e.type === 'mousedown') {
clicking = true;
self.isFocused = false; // awful hack to defuse the document mousedown listener
} else {
self.isFocused = true;
setTimeout(function() {
clicking = false; // wait until blur has been preempted
});
}
} else { // cleanup in case user right-clicked or dragged off the element
clicking = false;
self.isFocused = true;
}
});
// Intercept default handlers
self.$dropdown.off('mousedown click', '[data-selectable]').on('mousedown click', '[data-selectable]', function() {
if (!clicking) {
return self.onOptionSelect.apply(self, arguments);
}
});
self.$control_input.off('blur').on('blur', function() {
if (!clicking) {
return self.onBlur.apply(self, arguments);
}
});
}
});
To use it, you need to pass the plugin option to the selectize call (.selectize({plugins:['option_click']})) and add the clickable class to links in your dropdown template. (This is fairly specific. If there are nested elements, make sure clickable is on the one that will first see the mousedown event.)
Note that this is a fairly hackish approach that may have edge cases and could break at any time if something about how Selectize dispatches events changes. It would be better if Selectize itself would make this exception, but until the project catches up to its backlog and becomes more receptive to requests and PRs this may be the most practical approach.

ng-click on body breaks links and input fields on mobile with ngTouch

Not sure if anybody can help me here, I will probably have to come up with something different but I wanted to sound this issue since I didn't find anything like that here.
I have an app-like website with swipable menu, and I want it to go away whenever user taps somewhere outside the menu. So I've used ngTouch for swiping and attached ng-click="menuToggled = false" to close menu on click/tap.
However if ng-click is attached to body, links in the menu don't work and I cannot focus any of the input fields on the body.
This only happens on mobile devices: iOS or Android (or chrome device emulation).
As I said, I will probably have to think of another solution to close menu on tap, but this issue seems strange to me, perhaps somebody has some thoughts on it.
Here's a simple demo, as I said, it works on desktop but if you enable device emulation with F12 on Chrome you will not be able to focus input field, unless you hold mouse button:
http://jsfiddle.net/L85g3grs/
<body ng-app="myApp" ng-click="showMenu = false">
<input type="text">
<button type="button" ng-click="showMenu = true; $event.stopPropagation();">Show menu</button>
<div class="menu" ng-show="showMenu"></div>
</body>
I can't explain the real cause of your original problem.
seems that ng-click on body tag is not a good idea - i think it steals focus in some ways..
i have put together a someway complex solution - but it works on desktop and emulated mobile - tested in Firefox -
and handles the 'click + touch' problem:
http://jsfiddle.net/s_light/L85g3grs/6/
setup the click event on the button:
<button type="button" ng-click="menuShow($event)">
Show menu
</button>
and add the handling in your controller:
app.controller('MainController',[
'$scope',
'$document',
'$timeout',
function($scope, $document, $timeout) {
// using deep value so that there are no scope/childscope issues
$scope.menu = {
visible: false,
};
// our internal clickPrevent helper
var menu_clickPrevent = false;
function menuHide(event) {
console.log("menuHide");
// set menu visibility
$scope.menu.visible = false;
// we need a apply here so the view gets updated.
$scope.$apply();
// deactivate handler
$document.off('click', menuHide);
}
$scope.menuShow = function(event) {
console.log("menuShow", event);
// check if we are already handling a click...
if( !menu_clickPrevent ) {
// stop default and propagation so our hide handler is not called immediate
event.preventDefault();
event.stopPropagation();
// make menu visible
$scope.menu.visible = true;
// prevent 'double click' bugs on some touch devices
menu_clickPrevent = true;
$timeout(function () {
menu_clickPrevent = false;
}, 100);
// activate document wide click-handler
$document.on('click', menuHide);
}
};
}
]);

Button click not firing when leaving textarea

I am currently making a textarea that will auto expand. This seems to work fine however the button click never seems to fire when the textarea is expanded. I am also using angularjs. Here is my code
HTML:
<body >
<div my-dir></div>
</body>
Javascript:
var app = angular.module('myApp',[]);
app.directive('myDir', function(){
return {
restrict:'A',
template:'<textarea id="textarea1">'+'</textarea>' + '<button ng-click="clicked()">Click me</button><textarea id="textarea2"></textarea>',
link:function(scope){
scope.clicked = function(){
alert("click worked");
}
}
}
});
If anybody could help me find a workaround or explain why exactly this is happening that would be greatly appreciated.
Link to codepen: http://codepen.io/anon/pen/mJrjpP
When the textarea is focused the button moves down a bit, and when you click the button the textareas blur event fires first, moving the button up, so the click never happens because the button moved.
The solution is to make sure the button stays put, using CSS positioning, or as noted in the comment by gyantasaurus below
<button ng-mousedown="clicked()">Click me</button>
The problem is that, when the textarea loses focus, it resizes and the button moves so, if you've removed focus from the textarea by clicking on the button, the click event, which consists of a mousedown and mouseup event, will never fire as, when the mouse is released, the cursor is no longer over the button.
You can test this yourself by focussing on the textarea, clicking down on the button, moving your cursor to the button's new position and then releasing your mouse button.
One solution, therefore, would be simply to use the muosedown event, rather than the click event.
One possible solution is to move your button at the last and apply position:fixed to it.
template:'<textarea id="textarea1">'+'</textarea>' + '<textarea id="textarea2"></textarea><button class="btn" ng-click="clicked()">Click me</button>'
CSS:
.btn{
position: fixed;
}
But of-course this involves changing your element's position.
app.directive('myDir', function(){
return{
restrict:'A',
template:'<div ng-click="clicked()"><textarea id="textarea1">'+'</textarea>' + '<button >Click me</button><textarea id="textarea2"></textarea></div>',
link:function(scope){
scope.clicked = function(){
alert("click worked");
}
}
}
});
This is called event event propagation or event bubbling
Example: http://codepen.io/anon/pen/ZGpjZw

Detecting mouse click on div with Javascript without side-effects

I want to detect whenever someone clicks in a div (essentially I want to know when a user is interacting with a section of text on my site, be that by selecting some text or clicking on a link), but I don't want to interfere with what the user is doing.
If I put a onmousedown or onclick event on the div it ends up breaking selection, links, etc. Is there any way to catch these events without causing any interference ?
Onmousedown or onclick shouldn't interfere with anything as long as it doesn't return false;.
You can do this:
document.getElementById("spy-on-me").onmousedown = function () {
console.log("User moused down");
return true; // Not needed, as long as you don't return false
};
If you have other scripts that are attaching behaviour via this method on the page, then to prevent overriding them you can do:
var spyElement = document.getElementById("spy-on-me");
var oldMousedown = spyElement.onmousedown;
spyElement.onmousedown = function () {
console.log("User moused down");
if(oldMousedown) oldMousedown();
};
Yes, I suspect you are currently returning false at the end of the event binding, just don't do that or any of the things in this binding:
$('a').click(function(e) {
e.stopPropagation();
e.preventDefault();
return false;
});
If you do not do any of these three things, jQuery will not stop the event from bubbling up to the browser.
Edit: Sorry didn't realise it was a plain JavaScript question.
you can use do it by adding a event listener as well
var myNode= document.querySelector('.imagegrid');
myNode.addEventListener("click",function(e){
alert(e.target+" clicked");
});
A similar example is demonstrated here
Can't you simply add a click event to the div?
<div id="secretDiv" (click)="secretDivClick()">
then on your component:
secretDivClick() {
console.log('clicked');
}

Looking for a better workaround to Chrome select on focus bug

I have the same problem as the user in this question, which is due to this bug in Webkit. However, the workaround provided will not work for my app. Let me re-state the problem so that you don't have to go read another question:
I am trying to select all the text in a textarea when it gets focus. The following jQuery code works in IE/FF/Opera:
$('#out').focus(function(){
$('#out').select();
});
However, in Chrome/Safari the text is selected--very briefly--but then the mouseUp event is fired and the text is deselected. The following workaround is offered in the above links:
$('#out').mouseup(function(e){
e.preventDefault();
});
However, this workaround is no good for me. I want to select all text only when the user gives the textarea focus. He must then be able to select only part of the text if he chooses. Can anyone think of a workaround that still meets this requirement?
How about this?
$('#out').focus(function () {
$('#out').select().mouseup(function (e) {
e.preventDefault();
$(this).unbind("mouseup");
});
});
The accepted answer (and basically every other solution I found so far) does not work with keyboard focus, i. e. pressing tab, at least not in my Chromium 21. I use the following snippet instead:
$('#out').focus(function () {
$(this).select().one('mouseup', function (e) {
$(this).off('keyup');
e.preventDefault();
}).one('keyup', function () {
$(this).select().off('mouseup');
});
});
e.preventDefault() in the keyup or focus handler does not help, so the unselecting after a keyboard focus seems to not happen in their default handlers, but rather somewhere between the focus and keyup events.
As suggested by #BarelyFitz, it might be better to work with namespaced events in order to not accidentally unbind other event handlers. Replace 'keyup' with 'keyup.selectText' and 'mouseup' with 'mouseup.selectText' for that.
Why not simply:
$('#out').focus(function(){
$(this).one('mouseup', function() {
$(this).select();
});
});
Seems to work in all major browsers...
A very slightly different approach would be to separate the focus event from the mouse sequence. This works really nicely for me - no state variables, no leaked handlers, no inadvertent removal of handlers, and it works with click, tab, or programmatic focus. Code and jsFiddle below -
$('#out').focus(function() {
$(this).select();
});
$('#out').on('mousedown.selectOnFocus', function() {
if (!($(this).is(':focus'))) {
$(this).focus();
$(this).one('mouseup.selectOnFocus', function(up) {
up.preventDefault();
});
}
});
https://jsfiddle.net/tpankake/eob9eb26/27/
Make a bool. Set it to true after a focus event and reset it after a mouse up event. During the mouse up, if it's true, you know the user just selected the text field; therefore you know you must prevent the mouse up from happening. Otherwise, you must let it pass.
var textFieldGotFocus = false;
$('#out').focus(function()
{
$('#out').select();
textFieldGotFocus = true;
});
$('#out').mouseup(function(e)
{
if (textFieldGotFocus)
e.preventDefault();
});
$(document).mouseup(function() { textFieldGotFocus = false; });
It's important that you put the mouseup listener that resets the variable on document, since it's not guaranteed that the user will release the mouse button over the text field.
onclick="var self = this;setTimeout(function() {self.select();}, 0);"
Select the text before putting the focus on the input box.
$('#out').select().focus();
digitalfresh's solution is mostly there, but has a bug in that if you manually trigger .focus() using JS (so not using a click), or if you tab to the field, then you get an unwanted mouseup event bound - this causes the first click that should deselect the text to be ignored.
To solve:
var out = $('#out');
var mouseCurrentlyDown = false;
out.focus(function () {
out.select();
if (mouseCurrentlyDown) {
out.one('mouseup', function (e) {
e.preventDefault();
});
}
}).mousedown(function() {
mouseCurrentlyDown = true;
});
$('body').mouseup(function() {
mouseCurrentlyDown = false;
});
Note: The mouseup event should be on body and not the input as we want to account for the user mousedown-ing within the input, moving the mouse out of the input, and then mouseup-ing.
tpankake's answer converted to a reusable jQuery function..
(If you upvote this, please also upvote his answer)
Load the following AFTER loading the jQuery library:
$.fn.focusSelect = function () {
return this.each(function () {
var me = $(this);
me.focus(function () {
$(this).select();
});
me.on('mousedown.selectOnFocus', function () {
var me2 = $(this);
if (me2.is(':focus') === false) {
me2.focus();
me2.one('mouseup.selectOnFocus', function (up) {
up.preventDefault();
});
}
});
});
};
Use it like this:
$(document).ready(function () {
// apply to all inputs on the page:
$('input[type=text]').focusSelect();
// apply only to one input
$('#out').focusSelect();
});

Categories