Avoid loosing focus in children - javascript

Feel free to edit the question title if you find something better.
I am writing a little plugin for my website,
var main = document.getElementById("main"),
fakeInput = document.getElementById("fakeInput"),
zone = document.getElementById("zone"),
input = document.getElementById("input");
main.addEventListener("focusin", function() {
zone.style.display = "block";
});
main.addEventListener("focusout", function() {
zone.style.display = "none";
});
#main {
width: 200px;
}
#fakeInput {
background: blue;
height: 22px;
}
#zone {
background-color: red;
height: 100px;
text-align: center;
}
click in the blue zone :<br>
<div id="main" tabindex="-1">
<div id="fakeInput"></div>
<div id="zone" style="display:none">
<p>Click in the input :</p>
<input id="input" type="text" />
</div>
</div>
when I click on it, it opens (great !). I can click (almost) anywhere, the "focusout" event is not triggered.
Almost, because the input child steal the focus to its parent.
I read here : http://www.quirksmode.org/dom/events/blurfocus.html
focusin and focusout
Fire at the same time as focus and blur, but bubble
In my example, It seems that the focusin event of the input field never reach its parent.
I am looking for a way to fix this, any hints ?

The focusout event is sent to an element when it, or any element
inside of it, loses focus. This is distinct from the blur event in
that it supports detecting the loss of focus from parent elements (in
other words, it supports event bubbling).
Therefore there is a difference between this.
main.addEventListener("focusin", function() {
zone.style.display = "block";
});
main.addEventListener("focusout", function() {
zone.style.display = "none";
});
And this.
main.addEventListener("focus", function() {
zone.style.display = "block";
});
main.addEventListener("blur", function() {
zone.style.display = "none";
});
See this JSFiddle.

In my example, It seems that the focusin event of the input field never reach its parent.
In fact it does. Try to add some debug:
main.addEventListener("focusin", function(event) {
console.log('focusin', event.target)
zone.style.display = "block";
});
main.addEventListener("focusout", function(event) {
console.log('focusout', event.target)
zone.style.display = "none";
});
and you'll see

Related

How to trigger an event when there is selection inside a contentEditable div?

I want to trigger an event when the user selects some text/element in a contentEditable div. Accordingly I want to inactivate certain buttons in a Rich Text Editor.
There are some events which can be triggered based upon change of content. I only want an event when the user selects some content.
A relatively simplified version could look like this, depending on your needs you might need to handle browsers like old IE's etc. that do not support window.getSelection()!
const handleSelection = function() {
const btn = document.querySelector('#btn');
let selection = window.getSelection().anchorNode.textContent
.substring(
window.getSelection().extentOffset,
window.getSelection().anchorOffset
);
if (selection.length !== 0) {
btn.setAttribute('disabled', true);
} else {
btn.removeAttribute('disabled');
}
};
['mouseup', 'keyup', 'selectionchange'].forEach((e) => {
document.querySelector('#editable').addEventListener(e, handleSelection);
});
#btn[disabled] {
cursor: default;
opacity: 0.3;
}
<button type="button" id="btn">Button (disabled when something is selected)</button>
<div id="editable" contenteditable>
<p>I'm some content that is editable!</p>
</div>
You can add event listener to the element for mouseup event and get the selection from window after that:
<div contenteditable="true" id="editableDiv">Some content</div>
And the js:
document.getElementById("editableDiv").addEventListener("mouseup", function() {
var s = window.getSelection();
if(s == "something"){
//do your thing
}
}, false);

show an element only if contentEditable div is changed

$('#story').on('keypress', function(){
$('#btnsave').show();
});
#story{
background:gold;
min-height:54px;
padding:9px;
}
#btnsave{
display:none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='story' contentEditable='true'>lorem ipsum</div>
<br>
<button id='btnsave'>SAVE</div>
I need to show btnsave only if story is changed.
kyedown and keyup don't work because of funcional and other keys included.
keypress seems to be ok, except backspace and delete - when pressing - nothing happens.
What to do?
Change
$('#story').on('keypress', function(){
$('#btnsave').show();
});
To
document.getElementById("story").addEventListener("input", function() {
$('#btnsave').show();
}, false);
OR
$('#story').on('input', (e) => {
$('#btnsave').show();
});
Working Demo: https://codepen.io/OtakunityJL/pen/vVOvxV
as comment above you need to change keypress to input, and if you want to show only #btnsave when it different with previous content save original content as variable, then compare.
var oldContent = $('#story').text();
var myTimeout;
$('#story').on('input', function() {
clearTimeout(myTimeout);
myTimeout = setTimeout(function() {
if ($('#story').text() != oldContent) {
$('#btnsave').show();
}
else{
$('#btnsave').hide();
}
}, 200)
});
$('#btnsave').on('click', function(){
oldContent = $('#story').text();
$('#btnsave').hide();
})
#story{
background:gold;
min-height:54px;
padding:9px;
}
#btnsave{
display:none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='story' contentEditable='true'>lorem ipsum</div>
<br>
<button id='btnsave'>SAVE</div>
you can store the contents to a variable and check if it is different after blur() event. If it is different, then it changed. http://jsfiddle.net/maxofpower/a4QNB/850/
another scenario is that user maybe press key but doesn't change anything...
var content = $('#story').html();
$('#story').blur(function() {
if (content!=$(this).html()){
alert('change() called.');
content = $(this).html();
}
});
To handle this kind is issue, you can trigger an event handler when the div with attribute contenteditable set to true loose the focus. Which can be done by setting an event handler on the blur event. The other way is to set a timer which wait a certain number of second in which a user doesn't type any thing on they keybord and manualy trigger disable the contenteditable and set the display property of the button to block
var story = document.querySelector('#story');
var btn = document.querySelector('#btnsave');
story.addEventListener('blur', function(event){
btn.style.display = "block";
});
#btnsave {
display: none;
}
#story{
background:gold;
min-height:54px;
padding:9px;
}
<div id='story' contentEditable='true'>lorem ipsum</div>
<br>
<button id='btnsave'>SAVE</div>

Is there an event for when the title attribute shows the tooltip?

So, let's say I have this span with a title attribute:
<span title="Tooltip" id="mySpan">Span</span>
This shows the tooltip just fine, but I'd like to attach some kind of event listener for when the title shows with either JavaScript or JQuery. e.g. something like (obviously "title" isn't a valid event):
document.getElementById("mySpan").addEventListener("title", function () {
console.log("Tooltip shown");
});
I know that I could create my own tooltip with JavaScript and a mouseover event like this:
document.getElementById("mySpan").addEventListener("mouseover", function (event) {
console.log("Tooltip shown");
document.getElementById("myTooltip").style.display = 'block';
document.getElementById("myTooltip").style.top = (event.clientY + 5) + 'px';
document.getElementById("myTooltip").style.left = (event.clientX + 5) + 'px';
});
document.getElementById("mySpan").addEventListener("mouseout", function () {
document.getElementById("myTooltip").style.display = 'none';
});
#myTooltip {
position: absolute;
background-color: lightgray;
padding: 2px 4px;
font-size: 10px;
}
<span id="mySpan">Span</span>
<span id="myTooltip" style="display: none;">Tooltip</span>
But this means I have to recreate the style of the title attribute from scratch, which changes from browser to browser, and from OS to OS, and I'd really rather not change the standard tooltip that the user is used to seeing.
So, in summary, is it possible to attach some sort of event listener for when the title attribute displays?
You can use jQuery-ui Tooltip Widget
$( document ).tooltip();
$("#inputhelp").tooltip({
open: function( event, ui ) {
console.log('Text tooltip');
}
});
<link href="https://code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<p>
Anchor text
<input id="inputhelp" title="Input help">
</p>
Disclaimer: this isn't actually an event listener.
It's not the greatest of solutions, but it works. All it does is start a setTimeout for 0.5s (that was based on trial and error, couldn't find it in any docs) on mouseover, and cancel it when you mouseout. It works for me, but it might not work if the delay changes based on the browser / OS.
var titleShowTimeout;
document.getElementById("mySpan").addEventListener("mouseover", function (event) {
titleShowTimeout = setTimeout(function () {
console.log("Tooltip shown");
}, 500);
});
document.getElementById("mySpan").addEventListener("mouseout", function () {
clearTimeout(titleShowTimeout);
});
<span title="Tooltip" id="mySpan">Span</span>

iphone keyboard not hiding when tapping the screen

I have an input field with number type
<input type="number" pattern="[0-9]*"/>
In normal browsers, I'm typing some numbers and I'm tapping the screen, it hides iphone/ipad keyboard.
But this is not working if it is inside iframe. we need to click done button explicitly. This issue is only for iphone/ipad
This is an iframe issue. Any fix using Javascript/Jquery would be highly appreciated.
Updated
Tried
document.activeElement.blur();
and focusout when event triggered in javascript. none of them are working..
$(document).on('focusin', function(){
$('input').css("background-color", "green");
console.log('focusin!')
});
$(document).on('focusout', function(){
console.log('focusout!')
$('input').css("background-color", "yellow");
$('input').blur();
});
focusout is not calling inside iframe!
My question is **How to force close ipad/iphone keypad when input element is not focused using Javascript/Jquery?**
Answers will be rewarded as stated!
To remove the keyboard you need to lose the focus on your input.
document.activeElement.blur();
With this line you remove the focus and the keyboard disappear.
In your case, it's possible to add an event on your body, and stop this event if you click on an input.
$(document).ready(function () {
$('body').click(function () {
document.activeElement.blur();
console.log("blur");
});
$('input').click(function (event) {
event.stopPropagation();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text"/>
Update
I found this answer to get an active element into an iframe.
/**
* Return the active element in the main web or iframes
* #return HTMLElement
**/
function getActiveElement() {
var focused = false;
// Check if the active element is in the main web or no
if (document.body === document.activeElement ||
document.activeElement instanceof HTMLIFrameElement) {
// Search in iframes
$('iframe').each(function() {
var element = this.contentWindow.document.activeElement;
// If there is a active element
if (element !== this.contentWindow.document.body) {
focused = element;
return false; // Stop searching
}
});
} else focused = document.activeElement;
return focused; // Return element
}
With this function you can get the active element on the document or into an iframe.
After, you need to remove the focus on this element to hide the keyboard.
getActiveElement().blur();
html target <input> and <textarea>, iphone and ipad will not hide keyboard when taping on blank area ;but android will! we need to hide keyboard by hand -- it means to set the input blur;
here gose the code
$(function(){
var cacheInput = null;
var timer = null;
if(!isApple()){
return false;
}
$(document).on('focus','input',function(e){
cacheInput = e.target;
})
$(document).on('focus','textarea',function(e){
cacheInput = e.target;
})
$(document).on('touchend',function(e){
if(e.target.tagName!=='INPUT'&&e.target.tagName!=='TEXTAREA'){
if(cacheInput!==null){
timer = setTimeout(function(){
cacheInput.blur();
clearTimeout(timer);
},300)
}
}
})
function isApple(){
var ua = navigator.userAgent.toUpperCase();
var
ipad = ua.indexOf('IPAD')>-1,
ipod = ua.indexOf('IPOD')>-1,
iphone = ua.indexOf('IPHONE')>-1 ;
return ipad || ipod || iphone ;
}
})
github: https://github.com/wikieswan/iphone-input-blur
demo: http://wikieswan.github.io/iphone-input-blur
In an ionic application I used a directive on the body that intercepts the inputs.
MyApp.directive('dismissIosKeyboardOnClick', function () {
return function (scope, element, attrs) {
if (cordova.platformId=="ios") {
element.on("touchstart", function(e) {
var keyboardDoms = new Set(["INPUT","TEXTAREA","SELECT"]);
if( keyboardDoms.has(document.activeElement.nodeName) &&
!keyboardDoms.has(e.target.nodeName) )
document.activeElement.blur();
});
}
};
});
Index.html
<body ... dismiss-ios-keyboard-on-click></body>
Hope this'll solve your issue, it simply removes the focus on active element.
Using Javascript
document.activeElement.blur();
Using jQuery
$("#Clicked_button_id").click(function() {
$("#input_field_id").blur();
});
Try this.
This detects when you tab any element but the input and then blurs it.
$("html").children().not("#input_field_id").click(function(){
$("#input_field_id").blur();
});
I might possibly have a solution for your issue on iOS. My configuration is safari on iOS 8.3/5C.
From my experiments it seems to me that body element in Safari/iOS is not receptive to any click events. I am not able to explain why but it seems so.
So, what I have done is: put a wrapper div just inside body and allow it to receive the click.
Main.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>keyboard hide issue</title>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
//!SOLUTION
$('#body-wrapper').click(function(e) {
console.log('I got clicked');
});
});
</script>
<style>
/* ignore this. Its just styles. ... */
div#body-wrapper {
height: 200px;
width: 100%;
background-color: red;
}
input {
margin: 10px;
}
</style>
</head>
<body>
<div id="body-wrapper">
<input type="text" id="main-input1"/>
<input type="number" id="main-input2"/>
<input type="email" id="main-input3"/>
</div>
<iframe src="blur.html"></iframe>
</body>
</html>
blur.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Blur iFrame</title>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
//!SOLUTION
$('#wrapper').click(function(e) {
//do nothing
});
});
</script>
<style>
/* ignore this. Its just styles. ... */
div#wrapper {
height: 100px;
width: 100%;
background-color: yellow;
}
</style>
</head>
<body>
<div id="wrapper">
<input type="number" pattern="[0-9]*" id="main-input"/>
</div>
</body>
</html>
In both files I am able to hide the keyboard just fine. Again, I don't know the underlying reason but do let me know if you aren't able to replicate the solution.
I have not tried it on an iPad. Thanks ...
Well... You can give me that reward cause I just solved this problem using a very SIMPLE solution.
Step 1
:
Check if the input is currently in focus. I'll explain later why we need to add a delay on changing the value of inputFocused variable.
var inputFocused = false;
$('input').focus(function(){
setTimeout(function(){
inputFocused = true;
},100);
});
Step 2: Add an event listener if the screen or $(window) is tapped or clicked. If the window was tapped or clicked, check if the input is currently in focus. If true you must focus the window $(window).focus(), after focusing the window, the blur() function will now work! so you can now unfocus the input element then the keyboard will now hide automatically, then set the inputFocused variable to false again.
$(window).click(function(){
if(inputFocused == true){
$(window).focus();
var input = $('input');
input.blur();
inputFocused = false;
}
});`
SetTimeout explanation:
The $(window).click() event will trigger if the user tap or click anywhere on the screen (e.g. Button click, Input click, tap or click screen, etc). If you tap the input, at the same time setting the inputFocused to true, the $(window).click() event triggers then check if inputFocused is true then will run the code which hides the virtual keyboard. So it means that whenever you focus an input field the keyboard will hide and that'll be a problem.
That's why we're adding a delay so that the code inside if(inputFocused == true) will not run while we're focusing the input field and it will only run if the input field is currently on focus.
TRIED AND TESTED!

Focus / blur events on contenteditable elements

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.

Categories