Focus / blur events on contenteditable elements - javascript

I'm trying to listen to focus / blur events on span with contenteditable=true attribute.
So here's what I tried (jQuery) :
$('.editable').on('focus',function(){
var that = $(this),
defaultTxt = that.data('default');
that.html('');
});
$('.editable').on('blur',function(){
var that = $(this),
defaultTxt = that.data('default');
if(that.html() === ''){
that.html(defaultTxt);
}
});
But he doesn't seem to work, because span doesn't handle focus / blur. How can I achieve that anyway (IE8 support needed) ?

There are two ways to achieve this effect.
Approach 1: focusin and focusout
$('.editable').on('focusin', function() {
// your code here
});
$('.editable').on('focusout', function() {
// your code here
});
focusin and focusout are like focus and blur events, but unlike the latter, they are fired on almost(?) every element, and also bubble. focusin and focusout are a part of DOM level 3 Specification. Firefox 51 and older didn't support this due to a known bug, but Firefox 52 and above have full support.
Approach 2: click and focus
This only works if you have other focusable elements around your span. What you do is basically use the focus event on any other element as a blur handler.
$('.editable').on('click', function() {
// your code here
});
$('*').on('focus', function() {
// handle blur here
// your code here
});
I wouldn't recommend this approach in a large webapp, because browser performance will take a hit.

I have created a demo for you:
$('.editable').bind('click', function(){
$(this).attr('contentEditable',true);
});
$('.editable').bind('focus', function() {
var that = $(this);
//defaultTxt = that.data('default');
that.html('');
});
$('.editable').bind('blur', function() {
var that = $(this);
var defaultTxt = that.data('default');
if(that.html() === ''){
that.html(defaultTxt);
}
});
.editable{
padding: 5px;
border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<span class="editable" data-default="default">Some text</span>
I have changed your code, take a look it. Also now it's keeping the old value when lost the focus if you don't type anything.

Related

Resize event on multiple select control

I have a multiple select html element.
I want to catch the resize event, so I can store the latest value.
<select multiple="multiple" class="ra-multiselect-collection" style="resize: vertical; margin-top: 0px; margin-bottom: 5px; height: 527px;"></select>
The following code doesnt work:
$('.ra-multiselect-collection').on( 'resize', function(){
alert('resized');
});
Any ideas?
Well, I don't think that you can bind a resize event to a DOM element, but check this out:
$("select").on("mouseup", function()
{
var el = $(this);
if (el.data("old-height") == null || el.data("old-height") != el.height())
{
alert("resized!");
}
el.data("old-height", el.height());
}).data("old-height", $("select").height());
Fiddle
I have used mouseup event checking the old height stored in a data property with the current height on the moment of the event. If they are different, you get a resize event. Not a pretty beautiful workaround, but it seems to work.
I could not test on IE because it doesn't support that property and on Firefox, it works nice but it seems that you can double-click the resize corner and it returns to the initial size and this doesn't trigger the event actually.
In your case, as you're using a class to select those elements, you can do this:
$(".ra-multiselect-collection").each(function()
{
$(this).on("mouseup", function()
{
var el = $(this);
if (el.data("old-height") == null || el.data("old-height") != el.height())
{
alert("resized!");
}
el.data("old-height", el.height());
}).data("old-height", $(this).height());
});
Didn't tested but it should work.
Finally I used resizable function from jQuery UI
http://jqueryui.com/resizable/
This is my code:
$('.ra-multiselect-collection').first().resizable({
resize: function() {
alert('resized');
}
});
Maybe that .first() isn't necessary
You dont have resize event here, but u can store the size on mousedown event, and check if it changed on mouseup event

Stop element from disappearing when clicked

I'm writing a simple jQuery plugin that will dynamically place a div under a text box whenever it has focus. I've been able to get the position just about right in all the browsers.
I have to attach two event handlers as well on the focus and blur events of the textbox. And it works okay, but the problem is that the div that has been placed under the textbox closes even when we click on it. Now it makes sense why it would so happen, it's because the textbox loses focus, but is there a way I can stop it from happening?
I tried attaching this to the blur event handler -
if($(mainElem).is(":focus")) return;
where mainElem is the div that is shown below the textbox.
Here is a jsFiddle to illustrate the problem.
You are not going to be able to use the blur event if you want to place "clickable" elements in the div that shows. One way to solve this is to bind your event listener to a more global element like the document and then filter out the targets.
Here is a code sample:
$(document).on('click', function (e) {
var targetEl = e.target,
parent = $(e.target).parents()[0];
if (relElem[0] === targetEl || self[0] === targetEl || self[0] === parent) {
$(mainElem).show();
} else {
$(mainElem).hide();
}
});
Here is an update to your fiddle: http://jsfiddle.net/9YHKW/6/
This is a fiddle that I threw together for a project a while back: http://jsfiddle.net/MYcZx/4/
While it is not based off of your fiddle (and I do apologize) I believe that the functionality is much the same as what you're looking for. My example does not include input fields, but rather spans that hold the values. And while I'm not managing focus/blur, you could add a tabIndex attribute to the spans and then add a trigger on focus that would open the menu.
var $sub = $('.subscription');
$sub
.on('click', '.remove', function() {
$(this).parent().remove();
})
.on('click', 'li', function(e) {
var $this = $(this),
$parent = $this.parent(),
$options = $parent.children('li'),
$value = $parent.siblings('.value'),
isMulti = $parent.hasClass('multi'),
values = [];
if(!isMulti) {
$options.removeClass('active');
}
$this.toggleClass('active');
$options.filter('.active').each(function() {
values.push($(this).text());
});
$value.text(values.join(', ') || 'select');
$value[(values.length ? 'add' : 'remove') + 'Class']('set');
});
var $clone = $sub.clone(true);
$('.new')
.on('click', function() {
$(this).before($clone.clone(true));
});

checkbox button does not work well when clicked on quickly multiple times

I have a jsfiddle Here: http://jsfiddle.net/zAFND/616
Now if you open up fiddle in IE (I use IE9) and firefox, if you double click on a check box button, it turns it on but does not turn it off. But if you open it up in opera, safarai and chrome, it works fine if you double click or click in quick succession.
My question is how to allow quick succession clicks to work correctly in firefox and IE9?
Code:
HTML:
<div id="ck-button"><label>
<input type="checkbox" name="options[]" id="optionA" value="A" /><span>A</span></label>
</div>
CSS:
#ck-button {
margin:8px;
background-color:#EFEFEF;
border:1px solid #D0D0D0;
overflow:auto;
float:left;
position: relative;
}
#ck-button label {
float:left;
width:4.0em;
cursor:pointer;
}
#ck-button label span {
text-align:center;
padding:3px 0px;
display:block;
}
#ck-button label input {
position:absolute;
top:-20px;
}
#ck-button input:checked + span {
background-color:green;
color:white;
}
Jquery/javasscript:
$(document).ready(function(){
$("body").css("-webkit-user-select","none");
$("body").css("-moz-user-select","none");
$("body").css("-ms-user-select","none");
$("body").css("-o-user-select","none");
$("body").css("user-select","none");
});
This is a bug in Firefox. See Bug 608180 - Double/rapid clicking a checkbox label does not work as expected
IE has, for historical reasons (but fixed in more recent versions), a bugged event model that skips the second mousedown and click events on a double click. See bug 263 - beware of DoubleClick in IE.
I've made a plugin that fixes some bugs in jQuery UI button widget as well as working around the Firefox bug not long ago, shouldn't be hard to adapt it to your non-jQuery UI buttons.
Extracted the important part and adapted it for nested checkboxes inside labels:
(function () {
var mdtarg, //last label mousedown target
mdchecked, //checked property when mousedown fired
fixedLabelSelector = '.fixedLabelCheckbox'; //edit as you see fit
$(document).on('mousedown', fixedLabelSelector, function (e) {
//only left clicks will toggle the label
if (e.which !== 1) return;
mdtarg = this;
mdchecked = this.control ? this.control.checked : $(this).find('input')[0].checked;
//reset mdtarg after mouseup finishes bubbling; prevents bugs with
//incorrect mousedown-mouseup sequences e.g.
//down IN label, up OUT, down OUT, up IN
$(document).one('mouseup', function () {
mdtarg = null;
});
}).on('mouseup', fixedLabelSelector, function (e) {
if (e.which !== 1) return;
if (mdtarg === this) {
var ch = this.control || $(this).find('input')[0];
//the click event is supposed to fire after the mouseup so
//we wait until mouseup and click finish bubbling and check if it
//had the desired effect
setTimeout(function () {
if (mdchecked === ch.checked) {
//else patch it manually
ch.checked = !ch.checked;
$(ch).change();
}
}, 0);
}
});
}());
Fiddle tested in Firefox.
You have to add the fixedLabelCheckbox class to all labels containing checkboxes that you'd like to fix with the code above.
It will work regardless of where you put the script and it also fixes dynamically added checkboxes as long as the label has the corresponding delegated class/selector.
Note that if you're using other libraries, this may not fire the change handlers bound outside of jQuery.
If you don't feel like adding extra classes to your markup, you can use this version (more code and less performance):
(function ($) {
function getControl(lbl) { //fallback for non-HTML5 browsers if necessary
return lbl.control || (lbl.htmlFor ? $('input[id="'+lbl.htmlFor+'"]')[0] : $(lbl).find('input')[0]);
}
var mdtarg, //last label mousedown target
mdchecked; //checked property when mousedown fired
$(document).on('mousedown', 'label', function (e) {
//only left clicks will toggle the label
if (e.which !== 1) return;
var ch = getControl(this);
if (!ch || ch.type !== 'checkbox') return;
mdtarg = this;
mdchecked = ch.checked;
//reset mdtarg after mouseup finishes bubbling; prevents bugs with
//incorrect mousedown-mouseup sequences e.g.
//down IN label, up OUT, down OUT, up IN
$(document).one('mouseup', function () {
mdtarg = null;
});
}).on('mouseup', 'label', function (e) {
if (e.which !== 1) return;
if (mdtarg === this) {
var ch = getControl(this);
//the click event is supposed to fire after the mouseup so
//we wait until mouseup and click finish bubbling and check if it
//had the desired effect
setTimeout(function () {
if (mdchecked === ch.checked) {
//else patch it manually
ch.checked = !ch.checked;
$(ch).change();
}
}, 0);
}
});
}(jQuery));
Fiddle
As you can see from the code above, this version should work with both label's for attribute as well as nested inputs inside the label, without adding any extra markup.
About disabling selection: you can either put the user-select in the CSS as commented in your question, or, if browsers that don't support the user-select are also concerned, apply this answer on all labels that you want to have selection disabled.
You could add browser detection and then, if IE or Firefox, add the ondblclick event via JS to invert the checkbox.
You can't just set it unconditionally, since some browsers (Safari, Chrome) transmit two clicks and a dblclick, while others (IE, Firefox) transmit only one click and one dblclick. On the former, the two click events will invert the field twice. On the latter, only one click event fires and thus the field is only inverted once; to mitigate this, you need to make dblclick invert the field so that two clicks invert it an even number of times.
Hope this helps!!

Get the newly focussed element (if any) from the onBlur event.

I need to get the newly focussed element (if any) while executing an onBlur handler.
How can I do this?
I can think of some awful solutions, but nothing which doesn't involve setTimeout.
Reference it with:
document.activeElement
Unfortunately the new element isn't focused as the blur event happens, so this will report body. So you are gonna have to hack it with flags and focus event, or use setTimeout.
$("input").blur(function() {
setTimeout(function() {
console.log(document.activeElement);
}, 1);
});​
Works fine.
Without setTimeout, you can use this:
http://jsfiddle.net/RKtdm/
(function() {
var blurred = false,
testIs = $([document.body, document, document.documentElement]);
//Don't customize this, especially "focusIN" should NOT be changed to "focus"
$(document).on("focusin", function() {
if (blurred) {
var elem = document.activeElement;
blurred = false;
if (!$(elem).is(testIs)) {
doSomethingWith(elem); //If we reached here, then we have what you need.
}
}
});
//This is customizable to an extent, set your selectors up here and set blurred = true in the function
$("input").blur(function() {
blurred = true;
});
})();​
//Your custom handler
function doSomethingWith(elem) {
console.log(elem);
}
Why not using focusout event? https://developer.mozilla.org/en-US/docs/Web/Events/focusout
relatedTarget property will give you the element that is receiving the focus.

Detect which form input has focus using JavaScript or jQuery

How do you detect which form input has focus using JavaScript or jQuery?
From within a function I want to be able to determine which form input has focus. I'd like to be able to do this in straight JavaScript and/or jQuery.
document.activeElement, it's been supported in IE for a long time and the latest versions of FF and chrome support it also. If nothing has focus, it returns the document.body object.
I am not sure if this is the most efficient way, but you could try:
var selectedInput = null;
$(function() {
$('input, textarea, select').focus(function() {
selectedInput = this;
}).blur(function(){
selectedInput = null;
});
});
If all you want to do is change the CSS for a particular form field when it gets focus, you could use the CSS ":focus" selector. For compatibility with IE6 which doesn't support this, you could use the IE7 library.
Otherwise, you could use the onfocus and onblur events.
something like:
<input type="text" onfocus="txtfocus=1" onblur="txtfocus=0" />
and then have something like this in your javascript
if (txtfocus==1)
{
//Whatever code you want to run
}
if (txtfocus==0)
{
//Something else here
}
But that would just be my way of doing it, and it might not be extremely practical if you have, say 10 inputs :)
I would do it this way: I used a function that would return a 1 if the ID of the element it was sent was one that would trigger my event, and all others would return a 0, and the "if" statement would then just fall-through and not do anything:
function getSender(field) {
switch (field.id) {
case "someID":
case "someOtherID":
return 1;
break;
default:
return 0;
}
}
function doSomething(elem) {
if (getSender(elem) == 1) {
// do your stuff
}
/* else {
// do something else
} */
}
HTML Markup:
<input id="someID" onfocus="doSomething(this)" />
<input id="someOtherID" onfocus="doSomething(this)" />
<input id="someOtherGodForsakenID" onfocus="doSomething(this)" />
The first two will do the event in doSomething, the last one won't (or will do the else clause if uncommented).
-Tom
Here's a solution for text/password/textarea (not sure if I forgot others that can get focus, but they could be easily added by modifying the if clauses... an improvement could be made on the design by putting the if's body in it's own function to determine suitable inputs that can get focus).
Assuming that you can rely on the user sporting a browser that is not pre-historic (http://www.caniuse.com/#feat=dataset):
<script>
//The selector to get the text/password/textarea input that has focus is: jQuery('[data-selected=true]')
jQuery(document).ready(function() {
jQuery('body').bind({'focusin': function(Event){
var Target = jQuery(Event.target);
if(Target.is(':text')||Target.is(':password')||Target.is('textarea'))
{
Target.attr('data-selected', 'true');
}
}, 'focusout': function(Event){
var Target = jQuery(Event.target);
if(Target.is(':text')||Target.is(':password')||Target.is('textarea'))
{
Target.attr('data-selected', 'false');
}
}});
});
</script>
For pre-historic browsers, you can use the uglier:
<script>
//The selector to get the text/password/textarea input that has focus is: jQuery('[name='+jQuery('body').data('Selected_input')+']')
jQuery(document).ready(function() {
jQuery('body').bind({'focusin': function(Event){
var Target = jQuery(Event.target);
if(Target.is(':text')||Target.is(':password')||target.is('textarea'))
{
jQuery('body').data('Selected_input', Target.attr('name'));
}
}, 'focusout': function(Event){
var Target = jQuery(Event.target);
if(Target.is(':text')||Target.is(':password')||target.is('textarea'))
{
jQuery('body').data('Selected_input', null);
}
}});
});
</script>
You only need one listener if you use event bubbling (and bind it to the document); one per form is reasonable, though:
var selectedInput = null;
$(function() {
$('form').on('focus', 'input, textarea, select', function() {
selectedInput = this;
}).on('blur', 'input, textarea, select', function() {
selectedInput = null;
});
});
(Maybe you should move the selectedInput variable to the form.)
You can use this
<input type="text" onfocus="myFunction()">
It triggers the function when the input is focused.
Try
window.getSelection().getRangeAt(0).startContainer

Categories