I have to input elements #Core and #Price that cannot share a parent element. I need to test that both of them are not in focus and when they are not run a function.
I thought I could just do a check to make sure when one blurs the other isn't given focus like so:
$('#Core').blur(function() {
if(!$("#Price").is(":focus")) {
getSellingPrice()
}
});$('#Price').blur(function() {
if(!$("#Core").is(":focus")) {
getSellingPrice()
}
});
But this seems to fire even when I give focus to the other input node. My guess is that when the blur happens it checks the seconds focus before its yet been selected. But I am uncertain as to how to change the code to get the desired behavior of making sure both elements aren't in focus before triggering the function call.
Any ideas on how I might accomplish this or insight into why this current code isn't working are greatly appreciated.
You can just check that the active element is neither
var elems = $('#Core, #Price').blur(function() {
setTimeout(function() {
if ( elems.toArray().indexOf(document.activeElement) === -1 ) {
getSellingPrice();
}
});
});
But you'll need a timeout to make sure the focus is set etc.
Blurring an element passes focus to the body element before transferring it to a sub element clicked. Horrible as may be a one shot timer can be used to decouple blurring with checking what element was clicked. Concept code triggered by blur events (translate to your coding standards, jQuery and application as needed):
function blurred(el)
{
setTimeout(checkFocus, 4); // ( 4 == Firefox default minimum)
}
function checkFocus()
{ var a = document.getElementById("a");
var b = document.getElementById("b");
var infocus = document.activeElement === a || document.activeElement === b;
if(infocus)
console.log( "One of them is in focus");
else
console.log(" Neither is in focus");
}
HTML
a: <input id="a" type="text" onblur="blurred(this)"><br>
b: <input id="b" type="text" onblur="blurred(this)">
Related
*UPDATE:I am new to jQuery, as well as using XPath, and I am struggling with getting a proper working solution that will blur a dynamically created HTML element. I have an .onblur event hooked up (doesn't work as expected), and have tried using the $(document.activeElement), but my implementation might be incorrect. I would appreciate any help in creating a working solution, that will blur this element (jqInput) when a user clicks anywhere outside the active element. I have added the HTML and jQuery/JavaScript below.
Some ideas I have had:
(1) Use XPath to select a dynamic HTML element (jqInput), and then use jQuery's .onClick method to blur a this element, when a user clicks anywhere outside of the area of the XPath selected element.
(2) Use the $(document.activeElement) to determine where the .onblur should fire:
var thisTitle = input0;
var activeElement = $(document.activeElement);
if (thisTitle != activeElement) {
jqInput.hide();
_layout.viewHeaderTextInput.inputOnBlurHandler(canvasObj, jqHeaderText, jqInput);
}
I am open to all working solutions. And hopefully this will answer someone else's question in the future.
My challenge: Multiple elements are active, and the .onblur does not fire. See the image below:
NOTE: The <input /> field has focus, as well as the <div> to the left of the (the blue outline). If a user clicks anywhere outside that <input />, the blur must be applied to that element.
My Code: jQuery and JavaScript
This is a code snippet where the variable jqInput and input0 is created:
var jqInput = null;
if (jqHeaderText.next().hasClass("inline-editable"))
{
//Use existing input if it already exists
jqInput = jqHeaderText.next();
}
else
{
//Creaet a new editable header text input
jqInput = $("<input class=\"inline-editable\" type=\"text\"/>").insertAfter(jqHeaderText);
}
var input0 = jqInput.get(0);
//Assign key down event for the input when user preses enter to complete entering of the text
input0.onkeydown = function (e)
{
if (e.keyCode === 13)
{
jqInput.trigger("blur");
e.preventDefault();
e.stopPropagation();
}
};
This is my .onblur event, and my helper method to blur the element:
input0.onblur = function ()
{
_layout.viewHeaderTextInput.inputOnBlurHandler(canvasObj, jqHeaderText, jqInput);
};
inputOnBlurHandler: function (canvasObj, jqHeaderText, jqInput)
{
// Hide input textbox
jqInput.hide();
// Store the value in the canvas
canvasObj.headingText = jqInput.val();
_layout.updateCanvasControlProperty(canvasObj.instanceid, "Title", canvasObj.headingText, canvasObj.headingText);
// Show header element
jqHeaderText.show();
_layout.$propertiesContent.find(".propertyGridEditWrapper").filter(function ()
{
return $(this).data("propertyName") === "Title";
}).find("input[type=text]").val(canvasObj.headingText); // Update the property grid title input element
}
I have tried using the active element, but I don't think the implementation is correct:
var thisTitle = input0;
var activeElement = $(document.activeElement);
if (thisTitle != activeElement) {
jqInput.hide();
_layout.viewHeaderTextInput.inputOnBlurHandler(canvasObj, jqHeaderText, jqInput);
}
My HTML code:
<div class="panel-header-c">
<div class="panel-header-wrapper">
<div class="panel-header-text" style="display: none;">(Enter View Title)</div><input class="inline-editable" type="text" style="display: block;"><div class="panel-header-controls">
<span></span>
</div>
</div>
</div>
I thank you all in advance.
I'm developing a little function for my site where a sign in form is automatically shown in the navbar when the site is opened up, but only if a certain cookie has not been set. Then, after 10 seconds has passed, the form will disappear.
It is also supposed to stay open if the user has selected one of the form inputs OR if one of the inputs contain contents. (#user-pest or #pass-pest).
Most of it is working the way it is supposed to, however, even when one of the inputs is selected or contains contents, once 10 seconds has passed, the form will disappear from the page.
The following is the JavaScript (and jQuery) code that I am using (updated).
$(document).ready(function(){
// hide sign in form
function hideForm(){ // function for updating elements
$("#darknet-login-nav").css("display", "none");
$(".index-outer").css("height", "100px");
$(".index-inner").css("width", "438px");
$(".index-inner").css("top", "-10px");
$("#darknet-mast").css("font-size", "97px");
}
function hideFormSet(){ // function used for updating elements and setting cookie
hideForm();
document.cookie = "signinNav=set";
}
var checkDisplayed = getCookie("signinNav"); // get cookie contents
if(checkDisplayed == "set"){
hideForm(); // if cookie is set, hide the form
} else { // if it isn't
var hideInterval = setInterval(hideFormSet, 10000); // after 10 seconds, call hideFormSet function
if($("#user-pest").is(':focus') || $("#pass-pest").is(':focus')){
clearInterval(hideInterval); // if one of the inputs are focused on, stop the interval
} else {
hideInterval; // if they aren't focused, start the interval
}
}
});
and this is my simplified markup.
<div class="darknet-nav-login-form" id="darknet-login-nav">
<form action="" method="post">
<input type="text" name="username" id="user-pest" placeholder="Username" autocomplete="off"><br>
<input type="password" name="password" id="pass-pest" placeholder="Password" autocomplete="off"><br>
</form>
</div>
I'm still very new to JavaScript, so any pointers and fixes will be greatly appreciated.
EDIT: please check my above updated code.
Even when on of the inputs are focused, the interval will still continue, rather than stopping.
Thanks
If I understand your goal correctly, you also want to hide the form 10 seconds after the inputs lose focus.
In that case it's easier to bind to the focusin/focusout events to restart the timeout, otherwise when leaving an input just before the interval fires it is hidden much earlier than the timeout.
var inputs = $('#user-pest, #pass-pest'),
hideTimeout,
checkFocus = function(){
var hide = !inputs.is(':focus');
if(hide===!!hideTimeout)return;
if(hide)
hideTimeout = setTimeout(hideFormSet, 10000);
else
hideTimeout = clearTimeout(hideTimeout);
};
inputs.focusin(checkFocus).focusout(checkFocus);
checkFocus();
Sidenote, jQuery's is method checks if any of the elements in the jq array corresponds to the selector, so instead of a separate and/or, you can do: $('#user-pest, #pass-pest').is(':focus')
example Fiddle
Sidenote2, the (re)binding will occur twice because one input loses focus before the next one gains focus. This is not a problem in itself, but if the form only contains those 2 inputs, using event bubbling to check focus on the form itself might be one little step further optimized: inputs.parent().focusin(checkFocus).focusout(checkFocus);
You need an && in this line.
if(!$("#user-pest").is(':focus') || !$("#pass-pest").is(':focus')){
What you had before was
if( user-pest is not focused OR pass-pest is not focused)
A user can't focus both of them at once, thus this will always evaluate to true and hide will be set to true. Use the following:
if(!$("#user-pest").is(':focus') && !$("#pass-pest").is(':focus')){
Alternatively you could also use the following
if($("#user-pest").is(':focus') || $("#pass-pest").is(':focus')){
var hide = false;
} else {
var hide = true;
}
As pointed out in your comment there is also another problem, which I missed the first time.
The hide variable is set on page load, which happens instantly, and you most likely won't have had the time to focus either object yet. You should move the code that checks if it's focused to inside the timeout callback.
See this jsFiddle for the full code of a working example. Basically your timeout should check if the inputs are focused when run, not on page load, as seen in the following snippet.
setTimeout(function() {
if (!$("#user-pest").is(':focus') && !$("#pass-pest").is(':focus')) {
$("#darknet-login-nav").css("display", "none");
$(".index-outer").css("height", "100px");
$(".index-inner").css("width", "438px");
$(".index-inner").css("top", "-10px");
$("#darknet-mast").css("font-size", "97px");
document.cookie = "signinNav=set"; // set the cookie so the form doesn't appear when they come back later
}
}, 2000);
Here's a solution which ensures that the inputs are each empty and that they're not focused. Behaviour beyond the initial 10s timeout wasn't specified, so I've left the interval active - the hide behaviour will be invoked any time the timeout elapses and the conditions for hiding the header are met.
If you wish to make it a 'one-shot' timer, simply clearInterval in the intervalHandler function.
window.addEventListener('load', onDocLoaded, false);
var intervalHandle;
function onDocLoaded(evt)
{
intervalHandle = setInterval(intervalHandler, 2000);
}
function hideHeader()
{
document.getElementById('darknet-login-nav').classList.add('hidden');
}
// returns true/false
// true if the header should be hidden, false otherwise.
// Things that will prevent the header from being hidden area
// 0) content in the #user-pest input
// 1) content in the #pass-pest input
// 2) focus of either #user-pest or #pass-pest elements
function shouldHideHeader()
{
if (document.getElementById('user-pest').value != '')
return false;
if (document.getElementById('pass-pest').value != '')
return false;
if (document.activeElement == document.getElementById('user-pest'))
return false;
if (document.activeElement == document.getElementById('pass-pest'))
return false;
return true;
}
function intervalHandler()
{
if (shouldHideHeader())
hideHeader();
}
.darknet-nav-login-form
{
height: 42px;
}
.hidden
{
height: 0px;
overflow: hidden;
transition: height 2s;
}
<div class="darknet-nav-login-form" id="darknet-login-nav">
<form action="" method="post">
<input type="text" name="username" id="user-pest" placeholder="Username" autocomplete="off"/><br>
<input type="password" name="password" id="pass-pest" placeholder="Password" autocomplete="off"/><br>
</form>
</div>
I'm using the jQuery Highlight plugin to select some text on a web page.
I've hooked up selecting and deselecting with mouse events:
document.addEventListener('mouseup', doSelect);
document.addEventListener('mousedown', doDeselect);
The functions are:
function doSelect() {
var selectionRange = window.getSelection();
var selection = selectionRange.toString();
if (selection.trim().length > 0) {
$('body').highlight(selection);
}
}
function doDeselect() {
$('body').unhighlight();
}
Short and easy. The library searches for the selected text and wraps each occurrence in a <span> and so the text stands out.
It's working great, but I have two issues with how it behaves.
The problem is that once the span elements are applied, I cannot click hyperlinks (the ones that were found/selected), they don't react to clicks (I have to deselect the text first).
Once the span elements are added, the original selection is somehow lost, i.e. I cannot copy what I selected with CTRL+C.
These issues can be seen in this jsfiddle.
Why is this happening?
The code
The working demo is available here: jsfiddle
JavaScript
var $body = $('body');
var $copyArea = $('#copyArea');
document.addEventListener('mouseup', doSelect);
document.addEventListener('mousedown', doDeselect);
document.addEventListener('keydown', keyPressHandler);
function keyPressHandler(e) {
if(e.ctrlKey && e.keyCode == 67) {
$copyArea.focus().select();
}
}
function doSelect() {
var selectionRange = window.getSelection();
var selection = selectionRange.toString();
if (selection.trim().length > 0) {
$copyArea.val(selection);
$body.highlight(selection);
}
}
function doDeselect(e) {
var elem = $(e.target).parents('a');
if(elem.length == 0) {
$copyArea.val('');
$body.unhighlight();
}
}
HTML
Sample text to select.
<br/>Sample text to select.
<br/>Sample text to select.
<br/>google.com
google.com
<a href="http://google.com" target="_blank">
<span>
<span>google.com</span>
</span>
</a>
<textarea id="copyArea"></textarea>
CSS
.highlight {
background-color: #FFFF88;
}
#copyArea {
position:fixed;
top:-999px;
height:0px;
}
Part 1 - Clicking through the selection
Presumably, the reason clicking on a highlighted link doesn't work is because the process that disables the highlighting kicks in first and cancels the click.
To bypass that, we implement a condition that checks if the target element of the mousedown event has an a element as ancestor. If that is true, we simply do not execute $body.unhighlight();, allowing the click to pass through and open the link.
function doDeselect(e) {
var elem = $(e.target).parents('a');
if(elem.length == 0) {
$copyArea.val('');
$body.unhighlight();
}
}
Part 2 - Copying the selection
Presumably, again, the reason the selection is lost is because the document is modified by the highlighting, which introduces elements into the DOM.
My first idea was to reapply the selection after the modification was done. This became annoying and I went in a different direction, which allowed me to stumble upon this:
The Definitive Guide to Copying and Pasting in JavaScript
This offered a simple and efficient idea: using an hidden element that could contain selectable text.
Therefore, to allow copying the selected text that we highlighted despite having lost the original selection:
We add a hidden textarea element to our document.
<textarea id="copyArea"></textarea>
We get a reference to that element.
var $copyArea = $('#copyArea');
We add an event handler for the keydown event.
document.addEventListener('keydown', keyPressHandler);
We add the event handler.
function keyPressHandler(e) {
if(e.ctrlKey && e.keyCode == 67) {
$copyArea.focus().select();
}
}
We modify doSelect() to add some logic that will set the selection as the value of the textarea element, in the form of $copyArea.val(selection);.
function doSelect() {
var selectionRange = window.getSelection();
var selection = selectionRange.toString();
if (selection.trim().length > 0) {
$copyArea.val(selection);
$body.highlight(selection);
}
}
What does the handler do ? it captures the combination CTRL+C and focuses on the text in the hidden textarea, which ends up being copied by the keyboard command we just issued.
I have a webpage I'm building where I need to be able to select 1-9 members via a dropdown, which then provides that many input fields to enter their name. Each name field has a "suggestion" div below it where an ajax-fed member list is populated. Each item in that list has an "onclick='setMember(a, b, c)'" field associated with it. Once the input field loses focus we then validate (using ajax) that the input username returns exactly 1 database entry and set the field to that entry's text and an associated hidden memberId field to that one entry's id.
The problem is: when I click on the member name in the suggestion box the lose focus triggers and it attempts to validate a name which has multiple matches, thereby clearing it out. I do want it to clear on invalid, but I don't want it to clear before the onclick of the suggestion box name.
Example:
In the example above Paul Smith would populate fine if there was only one name in the suggestion list when it lost focus, but if I tried clicking on Raphael's name in the suggestion area (that is: clicking the grey div) it would wipe out the input field first.
Here is the javascript, trimmed for brevity:
function memberList() {
var count = document.getElementById('numMembers').value;
var current = document.getElementById('listMembers').childNodes.length;
if(count >= current) {
for(var i=current; i<=count; i++) {
var memberForm = document.createElement('div');
memberForm.setAttribute('id', 'member'+i);
var memberInput = document.createElement('input');
memberInput.setAttribute('name', 'memberName'+i);
memberInput.setAttribute('id', 'memberName'+i);
memberInput.setAttribute('type', 'text');
memberInput.setAttribute('class', 'ajax-member-load');
memberInput.setAttribute('value', '');
memberForm.appendChild(memberInput);
// two other fields (the ones next to the member name) removed for brevity
document.getElementById('listMembers').appendChild(memberForm);
}
}
else if(count < current) {
for(var i=(current-1); i>count; i--) {
document.getElementById('listMembers').removeChild(document.getElementById('listMembers').lastChild);
}
}
jQuery('.ajax-member-load').each(function() {
var num = this.id.replace( /^\D+/g, '');
// Update suggestion list on key release
jQuery(this).keyup(function(event) {
update(num);
});
// Check for only one suggestion and either populate it or clear it
jQuery(this).focusout(function(event) {
var number = this.id.replace( /^\D+/g, '');
memberCheck(number);
jQuery('#member'+number+'suggestions').html("");
});
});
}
// Looks up suggestions according to the partially input member name
function update(memberNumber) {
// AJAX code here, removed for brevity
self.xmlHttpReq.onreadystatechange = function() {
if (self.xmlHttpReq.readyState == 4) {
document.getElementById('member'+memberNumber+'suggestions').innerHTML = self.xmlHttpReq.responseText;
}
}
}
// Looks up the member by name, via ajax
// if exactly 1 match, it fills in the name and id
// otherwise the name comes back blank and the id is 0
function memberCheck(number) {
// AJAX code here, removed for brevity
if (self.xmlHttpReq.readyState == 4) {
var jsonResponse = JSON.parse(self.xmlHttpReq.responseText);
jQuery("#member"+number+"id").val(jsonResponse.id);
jQuery('#memberName'+number).val(jsonResponse.name);
}
}
}
function setMember(memberId, name, listNumber) {
jQuery("#memberName"+listNumber).val(name);
jQuery("#member"+listNumber+"id").val(memberId);
jQuery("#member"+listNumber+"suggestions").html("");
}
// Generate members form
memberList();
The suggestion divs (which are now being deleted before their onclicks and trigger) simply look like this:
<div onclick='setMember(123, "Raphael Jordan", 2)'>Raphael Jordan</div>
<div onclick='setMember(450, "Chris Raptson", 2)'>Chris Raptson</div>
Does anyone have any clue how I can solve this priority problem? I'm sure I can't be the first one with this issue, but I can't figure out what to search for to find similar questions.
Thank you!
If you use mousedown instead of click on the suggestions binding, it will occur before the blur of the input. JSFiddle.
<input type="text" />
Click
$('input').on('blur', function(e) {
console.log(e);
});
$('a').on('mousedown', function(e) {
console.log(e);
});
Or more specifically to your case:
<div onmousedown='setMember(123, "Raphael Jordan", 2)'>Raphael Jordan</div>
using onmousedown instead of onclick will call focusout event but in onmousedown event handler you can use event.preventDefault() to avoid loosing focus. This will be useful for password fields where you dont want to loose focus on input field on click of Eye icon to show/hide password
Since I experienced a very strange issue with different touch event libraries (like hammer.js and quo.js) I decided to develop the events I need on my own. The Issue I'm talking about is that a touch is recognized twice if a hyperlink appears on the spot where I touched the screen. This happens on iOS as well as Android.
Imagine an element on a web page that visually changes the content of the screen if you touch it. And if the new page shows a hyperlink (<a href="">) at the same spot where I touched the screen before that new hyperlink gets triggered as well.
Now I developed my own implementation and I noticed: I'm having the same problem! Now is the question: Why?
What I do is the following (yes, I'm using jQuery):
(see source code below #1)
This function is only used with some special elements, not hyperlinks. So hyperlinks still have the default behavior.
The problem only affects hyperlinks. It doesn't occur on other elements that use the event methods showed above.
So I can imagine that not a click event is fired on the same element I touch but a click 'action' is performed at the same spot where I touched the screen after the touch event was processed. At least this is what it feels like. And since I only catch the click event on the element I actually touch I don't catch the click event on the hyperlink - and actually that shouldn't be necessary.
Does anyone know what causes this behavior?
Full source codes
#1 - attatch event to elements
$elements is a jQuery object returned by $( selector );
callback is the function that should be called if a tap is detected
helper.registerTapEvent = function($elements, callback) {
var touchInfo = {
maxTouches: 0
};
function evaluate(oe) {
var isSingleTouch = touchInfo.maxTouches === 1,
positionDifferenceX = Math.abs(touchInfo.startX - touchInfo.endX),
positionDifferenceY = Math.abs(touchInfo.startY - touchInfo.endY),
isAlmostSamePosition = positionDifferenceX < 15 && positionDifferenceY < 15,
timeDifference = touchInfo.endTime - touchInfo.startTime,
isShortTap = timeDifference < 350;
if (isSingleTouch && isAlmostSamePosition && isShortTap) {
if (typeof callback === 'function') callback(oe);
}
}
$elements
.on('touchstart', function(e) {
var oe = e.originalEvent;
touchInfo.startTime = oe.timeStamp;
touchInfo.startX = oe.changedTouches.item(0).clientX;
touchInfo.startY = oe.changedTouches.item(0).clientY;
touchInfo.maxTouches = oe.changedTouches.length > touchInfo.maxTouches ? oe.touches.length : touchInfo.maxTouches;
})
.on('touchend', function(e) {
var oe = e.originalEvent;
oe.preventDefault();
touchInfo.endTime = oe.timeStamp;
touchInfo.endX = oe.changedTouches.item(0).clientX;
touchInfo.endY = oe.changedTouches.item(0).clientY;
if (oe.touches.length === 0) {
evaluate(oe);
}
})
.on('click', function(e) {
var oe = e.originalEvent;
oe.preventDefault();
});
}
#2 - the part how the page transition is done
$subPageElem is a jQuery object of the page that should be displayed
$subPageElem.prev() returns the element of the current page that hides (temporarily) when a new page shows up.
$subPageElem.css({
webkitTransform: 'translate3d(0,0,0)',
transform: 'translate3d(0,0,0)'
}).prev().css({
webkitTransform: 'translate3d(-5%,0,-100px)',
transform: 'translate3d(-5%,0,-100px)'
});
I should also mention that the new page $subPageElem is generated dynamically and inserted into the DOM. I. e. that link that gets triggered (but shouldn't) doesn't even exist in the DOM when I touch/release the screen.