How can I test this JavaScript effectively? - javascript

I've written a piece of code which acts on the event that a user highlights some text on a page. The code works fine (below) but my issue is how to test it effectively? Is there a way of mocking a user selecting text (specifically involving a mouseup event).
Maybe the issue is that checking if text is selected when a mouseup event occurs is not the best way to do this? Any insight is appreciated.
var note = {
mouseHandler : function(e){
selection = window.getSelection();
if (selection.toString() !== '') {
note.selection = selection;
note.setAttributes();
note.hideOverlay();
note.placeOverlay();
}
}
}
Ideally I'd like to be able to trigger this with test code so I can ensure note.placeOverlay() happens

So in Jasmine you would spy on window.getSelection and return a string in one case and none in the other. Then you would check that this what should happen in note.placeOver happens.
spyOn(window, 'getSelection').andReturn('someString')
note.mouseHandler();
//test what you expect here
spyOn(window, 'getSelection').andReturn('')
note.mouseHandler();
//test that nothings happens here
Maybe you can show what note.placeOver does, so I can complete the answer.

Related

EventListener('click') on document prevents other ('click') from working

I want to close a search menu (containing numerous elements) by clicking outside of it. For this I made a (onclick) function to open/close it with a link (which works) and a (onclick) function to detect clics outside of it, and close it. Problem is, my 1st function works on its own, but when I put the second one, nothing works. That's what i've got so far:
document.getElementById('link').addEventListener('click', function(e) {
menuframe = document.getElementsByClassName('searchframe')[0];
e.preventDefault(); // On bloque l'action par défaut de cet événement
if (!menuframe.classList.contains('displayed')) {
menuframe.classList.add('displayed');
} else {
menuframe.classList.remove('displayed');
link.blur(); }
}, false);
document.getElementById('all').addEventListener('click', function (e) {
var x = document.getElementById.e.target.id,
menuframe = document.getElementsByClassName('searchframe')[0];
if (menuframe.classList.contains('displayed') && !menuframe.contains(x))
menuframe.classList.remove('displayed'); }
}, false);
I assumed it had something to do with event bubbling/capture, tried to mess with true/false and e.stopPropagation(); without success. Or maybe my code is bad, I'm a newbie in JS. I tried googling everything I could think of, and tried various 'fixes' I found, without success.
To sum-up, how do I actually close a div when clicking outside? (without jQuery if possible, I do not know the syntax at all)
Edit: added '{' after 'if' on line 14 and changed line 12 by " var x = document.getElementById(e.target.id) " as Jameel suggested. Now the e.preventDefault works on the 1st function, but that's still it.
You need to pass the e.target.id to the document.getElementById method like this:
var x = document.getElementById(e.target.id)
Have you checked your javascript console for errors?
Based on your stated question (see below) i assume your first click opens/shows a new layer. Does the new element maybe overlap the underlying (your so called outside) element so that it never receives the second click? You could try to colorize the areas to check if something overlaps, or use the debugging tools of your browser.
To sum-up, how do I actually close a div when clicking outside?

jQuery and textarea change events

i just met a strange behavior jQuery and can't figure out any more or less slight solution.
Say, i have textarea and button. I want to disable button if textarea is empty.
For this i have a handler which does this job.
Here is the code:
// textarea control
var $textarea = $('#myTextarea');
var $button = $('#myButton');
$textarea.on('input propertychange', function() {
if($textarea.val().length > 0) {
$button.removeClass('disabled').removeAttr('disabled');
} else {
$button.addClass('disabled').attr('disabled', 'disabled');
}
});
$button.on('click', function() {
if ($button.attr("disabled") != null) {
console.log('Disabled!');
return false;
} else {
// do some stuff and eventually erase textarea
$textarea.val('');
}
});
My trouble is when i erase textarea (the end of the code) it doesn't disable the button. Any ideas would be appreciated (actually it's slight adaptation of my code but the situation reflected pretty good, hope it would be clear for you, thanks!)
UPD
Nothing found on stackoverflow doesn't help.
UPD2
As i said in the begin, i was looking not for workaround like force trigger event, i thought it's possible to catch any event fired by $textarea.val(); Sure #Madbreaks and #epascarello 's solutions work pretty good, thanks guys.
Maybe just add this:
else {
// do some stuff and eventually erase textarea
$textarea.val('');
// notify
$textarea.trigger('propertychange');
}
problem is setting values with JavaScript does not normally trigger the events, but you can do it yourself.
$textarea.val('').trigger("input");
Make sure you trim the value of your textarea and you dont have an white spaces when you get the length of the .val(). Also in case you use a text editor like ckEditor make sure you read their documentation on how to retrieve the text being written.
Recommend using this jQueryTextChange lib. Its a cross browser consistent one. The usages are clearly elaborated on the home page. http://zurb.com/playground/jquery-text-change-custom-event

How to deslect highlighted text in textbox if already highlighted?

So I have this very simple JS function that selects all the text in the ASP.NET texbox (input):
function selectAllText(textbox) {
textbox.focus();
textbox.select();
}
..and it gets called like this on the click event:
$("#<%=Textbox1.ClientID %>").click(function () { selectAllText(jQuery(this)) });
The problem is no matter how many times a user clicks in the text box all of the text is always selected. I understand why this is occuring (based on the way my code above is), but it doesn't work well when the user tries to click in the middle of a word to get the cursor back to make a modification to the text.
How do I modify this JS to tell if the text is already highlighted and then deselect the text? This was on subsiquient click, the user can get the single cursor on a precise clicked location to make a modification.
I am trying to get the documentation on the .select() method to see if I could do if(!textbox.select()), but I am having a hard time finding it, so post any doc links as well if you have them.
EDIT: This problem and need for a workaround seems to be for IE (I am using IE9). In Chrome the behavior by default is what I need, but this is for an intranet application that runs on IE, so it appears I need an explicit workaround.
Thanks!
Is it necessary to do the .focus() in this function? You could instead attach a simple .select(); to the onfocus event (.bind('focus', function(){..})): http://jsfiddle.net/EGHzj/
Try this
$(document).ready(function(){
$('input').bind('click',function(){
if($(this).hasClass('selected')){
$(this).toggleClass('selected');
}else{
this.focus();
this.select();
$(this).toggleClass('selected');
}
});
});
This should work in IE also,
http://jsfiddle.net/rAqgw/7/
function selectAllText(textbox) {
// if there isn't selected text.
if (textbox[0].selectionEnd) {
textbox.focus();
textbox.select();
}
}
$('#txt').click(function() {
selectAllText($(this));
});
Live DEMO
​

Any event triggered on autocomplete?

I have a pretty simple form. When the user types in an input field, I want to update what they've typed somewhere else on the page. This all works fine. I've bound the update to the keyup, change and click events.
The only problem is if you select an input from the browser's autocomplete box, it does not update. Is there any event that triggers when you select from autocomplete (it's apparently neither change nor click). Note that if you select from the autocomplete box and the blur the input field, the update will be triggered. I would like for it to be triggered as soon as the autocomplete .
See: http://jsfiddle.net/pYKKp/ (hopefully you have filled out a lot of forms in the past with an input named "email").
HTML:
<input name="email" />
<div id="whatever"><whatever></div>
CSS:
div {
float: right;
}
Script:
$("input").on('keyup change click', function () {
var v = $(this).val();
if (v) {
$("#whatever").text(v);
}
else {
$("#whatever").text('<whatever>');
}
});
I recommending using monitorEvents. It's a function provide by the javascript console in both web inspector and firebug that prints out all events that are generated by an element. Here's an example of how you'd use it:
monitorEvents($("input")[0]);
In your case, both Firefox and Opera generate an input event when the user selects an item from the autocomplete drop down. In IE7-8 a change event is produced after the user changes focus. The latest Chrome does generate a similar event.
A detailed browser compatibility chart can be found here:
https://developer.mozilla.org/en-US/docs/Web/Events/input
Here is an awesome solution.
$('html').bind('input', function() {
alert('test');
});
I tested with Chrome and Firefox and it will also work for other browsers.
I have tried a lot of events with many elements but only this is triggered when you select from autocomplete.
Hope it will save some one's time.
Add "blur". works in all browsers!
$("input").on('blur keyup change click', function () {
As Xavi explained, there's no a solution 100% cross-browser for that, so I created a trick on my own for that (5 steps to go on):
1. I need a couple of new arrays:
window.timeouts = new Array();
window.memo_values = new Array();
2. on focus on the input text I want to trigger (in your case "email", in my example "name") I set an Interval, for example using jQuery (not needed thought):
jQuery('#name').focus(function ()
{
var id = jQuery(this).attr('id');
window.timeouts[id] = setInterval('onChangeValue.call(document.getElementById("'+ id +'"), doSomething)', 500);
});
3. on blur I remove the interval: (always using jQuery not needed thought), and I verify if the value changed
jQuery('#name').blur(function ()
{
var id = jQuery(this).attr('id');
onChangeValue.call(document.getElementById(id), doSomething);
clearInterval(window.timeouts[id]);
delete window.timeouts[id];
});
4. Now, the main function which check changes is the following
function onChangeValue(callback)
{
if (window.memo_values[this.id] != this.value)
{
window.memo_values[this.id] = this.value;
if (callback instanceof Function)
{
callback.call(this);
}
else
{
eval( callback );
}
}
}
Important note: you can use "this" inside the above function, referring to your triggered input HTML element. An id must be specified in order to that function to work, and you can pass a function, or a function name or a string of command as a callback.
5. Finally you can do something when the input value is changed, even when a value is selected from a autocomplete dropdown list
function doSomething()
{
alert('got you! '+this.value);
}
Important note: again you use "this" inside the above function referring to the your triggered input HTML element.
WORKING FIDDLE!!!
I know it sounds complicated, but it isn't.
I prepared a working fiddle for you, the input to change is named "name" so if you ever entered your name in an online form you might have an autocomplete dropdown list of your browser to test.
Detecting autocomplete on form input with jQuery OR JAVASCRIPT
Using: Event input. To select (input or textarea) value suggestions
FOR EXAMPLE FOR JQUERY:
$(input).on('input', function() {
alert("Number selected ");
});
FOR EXAMPLE FOR JAVASCRIPT:
<input type="text" onInput="affiche(document.getElementById('something').text)" name="Somthing" />
This start ajax query ...
The only sure way is to use an interval.
Luca's answer is too complicated for me, so I created my own short version which hopefully will help someone (maybe even me from the future):
$input.on( 'focus', function(){
var intervalDuration = 1000, // ms
interval = setInterval( function(){
// do your tests here
// ..................
// when element loses focus, we stop checking:
if( ! $input.is( ':focus' ) ) clearInterval( interval );
}, intervalDuration );
} );
Tested on Chrome, Mozilla and even IE.
I've realised via monitorEvents that at least in Chrome the keyup event is fired before the autocomplete input event. On a normal keyboard input the sequence is keydown input keyup, so after the input.
What i did is then:
let myFun = ()=>{ ..do Something };
input.addEventListener('change', myFun );
//fallback in case change is not fired on autocomplete
let _k = null;
input.addEventListener( 'keydown', (e)=>_k=e.type );
input.addEventListener( 'keyup', (e)=>_k=e.type );
input.addEventListener( 'input', (e)=>{ if(_k === 'keyup') myFun();})
Needs to be checked with other browser, but that might be a way without intervals.
I don't think you need an event for this: this happens only once, and there is no good browser-wide support for this, as shown by #xavi 's answer.
Just add a function after loading the body that checks the fields once for any changes in the default value, or if it's just a matter of copying a certain value to another place, just copy it to make sure it is initialized properly.

Why is my onchange function called twice when using .focus()?

TLDR
Check this example in chrome.
Type someting and press tab. see one new box appear
type something and press enter. see two new boxes appear, where one is expected.
Intro
I noticed that when using enter rather then tab to change fields, my onchange function on an input field was firing twice. This page was rather large, and still in development (read: numerous other bugs), so I've made a minimal example that shows this behaviour, and in this case it even does it on 'tab'. This is only a problem in Chrome as far as I can tell.
What it should do
I want to make a new input after something is entered into the input-field. This field should get focus.
Example:
javascript - needing jquery
function myOnChange(context,curNum){
alert('onchange start');
nextNum = curNum+1;
$(context.parentNode).append('<input type="text" onchange="return myOnChange(this,'+nextNum+')" id="prefix_'+nextNum+'" >');
$('#prefix_'+nextNum).focus();
return false;
}
HTML-part
<div>
<input type="text" onchange="return myOnChange(this,1);" id="prefix_1">
</div>
the complete code is on pastebin. you need to add your path to jquery in the script
A working example is here on jFiddle
The onchange gets called twice: The myOnChange function is called, makes the new input, calls the focus(), the myOnChange gets called again, makes a new input, the 'inner' myOnChange exits and then the 'outer' myOnchange exits.
I'm assuming this is because the focus change fires the onchange()?. I know there is some difference in behaviour between browsers in this.
I would like to stop the .focus() (which seems to be the problem) to NOT call the onchange(), so myOnChange() doesn't get called twice. Anybody know how?
There's a way easier and more reasonable solution. As you expect onchange fire when the input value changes, you can simply explicitly check, if it was actually changed.
function onChangeHandler(e){
if(this.value==this.oldvalue)return; //not changed really
this.oldvalue=this.value;
// .... your stuff
}
A quick fix (untested) should be to defer the call to focus() via
setTimeout(function() { ... }, 0);
until after the event handler has terminated.
However, it is possible to make it work without such a hack; jQuery-free example code:
<head>
<style>
input { display: block; }
</style>
<body>
<div></div>
<script>
var div = document.getElementsByTagName('div')[0];
var field = document.createElement('input');
field.type = 'text';
field.onchange = function() {
// only add a new field on change of last field
if(this.num === div.getElementsByTagName('input').length)
div.appendChild(createField(this.num + 1));
this.nextSibling.focus();
};
function createField(num) {
var clone = field.cloneNode(false);
clone.num = num;
clone.onchange = field.onchange;
return clone;
}
div.appendChild(createField(1));
</script>
I can confirm myOnChange gets called twice on Chrome. But the context argument is the initial input field on both calls.
If you remove the alert call it only fires once. If you are using the alert for testing only then try using console instead (although you need to remove it for testing in IE).
EDIT: It seems that the change event fires twice on the enter key. The following adds a condition to check for the existence of the new field.
function myOnChange(context, curNum) {
nextNum = curNum+1;
if ($('#prefix_'+nextNum).length) return false;// added to avoid duplication
$(context.parentNode).append('<input type="text" onchange="return myOnChange(this,'+nextNum+')" id="prefix_'+nextNum+'" >');
$('#prefix_'+nextNum)[0].focus();
return false;
}
Update:
The $('#prefix_'+nextNum).focus(); does not get called because focus is a method of the dom object, not jQuery. Fixed it with $('#prefix_'+nextNum)[0].focus();.
The problem is indeed that because of the focus(), the onchange is called again. I don't know if this is a good sollution, but this adding this to the function is a quick sollution:
context.onchange = "";
(The onchange is called again, but is now empty. This is also good because this function should never be called twice. There will be some interface changes in the final product that help with problems that would arise from this (mistakes and all), but in the end this is something I probably would have done anyway).
sollution here: http://jsfiddle.net/k4WKH/2/
As #johnhunter says, the focus does not work in the example, but it does in my complete code. I haven't looked into what's going on there, but that seems to be a separate problem.
maybe this some help to anybody, for any reason, in chrome when you attach an event onchage to a input text, when you press the enterkey, the function in the event, do it twice, i solve this problem chaged the event for onkeypress and evaluate the codes, if i have an enter then do the function, cause i only wait for an enterkey user's, that not works for tab key.
input_txt.onkeypress=function(evt){
evt = evt || window.event;
var charCode = evt.which || evt.keyCode;
if(charCode === 13) evaluate( n_rows );
};
Try this example:
var curNum = 1;
function myOnChange( context )
{
curNum++;
$('<input type="text" onchange="return myOnChange( this )" id="prefix_'+ curNum +'" >').insertAfter( context );
$('#prefix_'+ curNum ).focus();
return false;
}
jsFiddle.

Categories