I have a button on an Ajax form that when pressed, indicates to the user that it is "searching" kind of a small "throbber" to keep the user occupied while the DB is being queried.
When the submit button is clicked it goes to a function. That function will disable the button, gather up what is filled out on the form, perform the Ajax call (non-asynchronous - meaning the script is to WAIT on the call to complete before moving on to the next line of code) and then re-enable the button.
Like so:
function CheckForm()
{
disableButton(document.getElementById("save"));
....... lots of instructions in here ........
....... Loops through every form el .........
//Ajax called
CallAjax(sUrls, sParams);
enableButton(document.getElementById("save"));
}
function disableButton(element)
{
try
{
disabledEl = element.id
disabledElOrigTxt = element.value;
element.value = ' Loading...'
addClass(element, "pleaseWait");
addClass(document.myForm.reset, "resetWait");
element.disabled = true;
document.myForm.reset.disabled = true;
//return true;
}
catch(e)
{
////SHHHHHH
}
}
function enableButton(element, timer)
{
try
{
if(element.value == ' Loading...')
element.value = disabledElOrigTxt;
removeClass(element, "pleaseWait");
removeClass(document.myForm.reset, "resetWait");
element.disabled = false;
document.myForm.reset.disabled = false;
clearTimeout(timer);
return true;
}
catch(e)
{
////SHHHHHH
}
}
function hasClass(element, className)
{
var classes = element.className.split(' ');
var len = classes.length;
for (var i=0; i<len; i++)
{
if (classes[i] == className)
return true;
}
return false;
}
function addClass(element, className)
{
if (!hasClass(element, className))
element.className = (element.className == '' ? className : element.className + ' ' + className);
}
function removeClass(element, className)
{
var newValue = '';
var classes = element.className.split(' ');
var len = classes.length;
for (var i=0; i<len; i++)
{
if (classes[i] != className)
newValue += newValue.length ? ' ' + classes[i] : classes[i];
}
element.className = newValue;
}
This works in Mozilla, IE, but NOT CHROME. Anyone have any idea why?
If I modify disableButton() and make it alert("hi") on the last line of the try, in Chrome I can observe the button change.... but ONLY if I throw an alert in there to stop the script. Obviously that is not what I want to do.
Maybe your CallAjax() function works asynchronously in Chrome?
Another possibility, maybe Chrome processes it so fast you don't notice the changes?
Related
I'm starting to write jQuery in Vanilla JS and my selectors work but when I call my append function on the HTML element I get an "is not a function" error.
var $ = function(){
this.select = function(input) {
if (input.split("")[0] == "#") {
input = input.slice(1, input.length)
return document.getElementById(input)
}
else if (input.split("")[0] == ".") {
input = input.slice(1, input.length)
return document.getElementsByClassName(input)
}
else {
return document.getElementsByTagName(input)
}
},
this.append = function(text) {
return this.innerhtml = this.innerhtml + text
}
};
my console attempts:
var myQuery = new $();
returns undefined
myQuery.select("#testspan")
returns my span tag here
myQuery.select("#testspan").append("hellohello")
returns error
VM2207:1 Uncaught TypeError: myQuery.select(...).append is not a function(…)
From your snippet the return of each of the select method return a DOM element (or collection). Really what you would like to do is called Chaining where the result of the method returns the original object. Therefore you can keep calling additional methods on the same object.
Now in your example you are going to need a collection of elements (nodes) somewhere the object can then access again. Here is a simple example.
var $ = function () {
this.nodes = [];
this.select = function (input) {
var self = this;
if (input.split("")[0] == "#") {
input = input.slice(1, input.length)
var node = document.getElementById(input);
if (node)
this.nodes.push(node);
}
else if (input.split("")[0] == ".") {
input = input.slice(1, input.length)
Array.prototype.slice.call(document.getElementsByClassName(input), 0).forEach(function (node) {
self.nodes.push(node);
});
}
else {
Array.prototype.slice.call(document.getElementsByTagName(input), 0).forEach(function (node) {
self.nodes.push(node);
});
}
return this;
},
this.append = function (text) {
this.nodes.forEach(function (i) {
i.innerHTML += text;
});
return this;
}
};
Sample Html:
<p id="test">This is test </p>
<p>This is number to</p>
Console (Chrome):
$ = new $()
$ {nodes: Array[0]}
$.select('p').append('hi')
Now a little issue here is you are (in the console) setting $ = new $() which effectivly overwrites the ability to call new $() again in the same script. I have provided a fiddle below that renames this to myQuery. Also changed that every time you call select will clear the node array.
Revised:
var myQuery = function () {
this.nodes = [];
this.select = function (input) {
this.nodes = [];
var self = this;
if (input.split("")[0] == "#") {
input = input.slice(1, input.length)
var node = document.getElementById(input);
if (node)
this.nodes.push(node);
}
else if (input.split("")[0] == ".") {
input = input.slice(1, input.length)
Array.prototype.slice.call(document.getElementsByClassName(input), 0).forEach(function (node) {
self.nodes.push(node);
});
}
else {
Array.prototype.slice.call(document.getElementsByTagName(input), 0).forEach(function (node) {
self.nodes.push(node);
});
}
return this;
},
this.append = function (text) {
this.nodes.forEach(function (i) {
i.innerHTML += text;
});
return this;
}
};
$ = new myQuery();
$.select('p').append(' test selection by tag name ');
$ = new myQuery();
$.select('.p1').append(' test selection by class ');
$ = new myQuery();
$.select('#p1').append(' test selection by id ');
$ = new myQuery();
$.select('#p2').append(' test selection by id ').append('and then chanined').select('.p2').append(' still chaining');
Fiddle: https://jsfiddle.net/kxwt9gmg/
You need to change up your approach a bit. You are wanting to store a result and call a method on it. You can ONLY call a method that that particular object has. That object you are returning, the raw html element, doesn't have that method. What you want to do is store the html element and then return an OBJECT that performs operations on what was stored. You can accomplish this using closure. For example:
function miniQuery(input){
function elementIterate(collection, action){
for (var i = elements.length -1; i >= 0; i-- ){
collection[i].style.display = action;
}
}
var isCollection = function(element){
if(element instanceof HTMLCollection){
return true
} else{
return false
}
}
function findElement(element){
if (element.startsWith("#")) {
// id element selector
return document.getElementById(element.substring(1));
} else if (element.startsWith(".")) {
// class element selector
return document.getElementsByClassName(element.substring(1));
} else {
// tag element selector
return document.getElementsByTagName(element);
};
}
if (input != undefined) {
var _this = this;
this.element = findElement(input);
var elements = findElement(input);
}
return {
append: function(content, position = 'beforeend'){
var elements = _this.element;
if (isCollection(elements)) {
for(var i = elements.length -1; i >= 0; i--){
elements[i].insertAdjacentHTML(position, content)
}
}else{
elements.insertAdjacentHTML(position, content);
}
}
}
}
function $(input){
return selector(input);
}
function selector(input){
var query = new miniQuery(input);
return query;
}
i have a jsp page and call a JS function which is in some abc.js file from this JSP page.
i have included this js file to jsp page.
JSP JavaScript Code:-
function doFinish(tableId, col, field)
{
var oldselectedCells = "";
var selItemHandle = "";
var selRightItemHandle = "";
var left = -1;
var right = -1;
// Get the table (tBody) section
var tBody = document.getElementById(tableId);
// get field in which selected columns are stored
var selectedCellsFld = document.getElementById(tableId + datatableSelectedCells);
selectedCellsFld.value = oldselectedCells;
for (var r = 0; r < tBody.rows.length; r++)
{
var row = tBody.rows[r];
if (row.cells[col].childNodes[0].checked == true)
{
selectedCellsFld.value = oldselectedCells +
row.cells[col].childNodes[0].id;
selItemHandle = row.cells[col].childNodes[0].value
oldselectedCells = selectedCellsFld.value + datatableOnLoadDivider;
left = selItemHandle.indexOf("=");
right = selItemHandle.length;
selRightItemHandle = selItemHandle.substring(left+1,right);
var index=getColumnIndex(tBody,"Name");
if(index!=null)
{
if(field == 1)
{
window.opener.document.TemplateForm.eds_asbactionscfg_item_handle_child_physpart.value = selRightItemHandle;
window.opener.document.TemplateForm.ChildPhysicalPart.value = row.cells[index].childNodes[0].innerHTML;
}
else if (field == 2)
{
window.opener.document.TemplateForm.eds_asbactionscfg_dev_doc_item_handle_name.value = selRightItemHandle;
window.opener.document.TemplateForm.DeviationObject.value = row.cells[index].childNodes[0].innerHTML;
}
else if (field == 3)
{
window.opener.document.TemplateForm.eds_asbactionscfg_dev_doc_item_handle_name.value = selRightItemHandle;
window.opener.document.TemplateForm.DeviationObject.value = row.cells[index].childNodes[0].innerHTML;
}
}
}
}
window.close();
}
JS Code:-
function getColumnIndex(tBody,columnName)
{
var cells = tBody.parentNode.getElementsByTagName('th');
for (var i=0;i<cells.length; i++)
{
if(cells[i].hasChildNodes())
{
if(cells[i].childNodes[0].innerHTML.replace(/(\r\n|\n|\r)/gm ,"").trim() == columnName)
{
return i;
}
}
}
}
i had debug this code with firebug & calling getColumnIndex(tBody,columnName) function works fine but when it return to caller the var index=getColumnIndex(tBody,"Name"); the index value is "undefine".
suggest some solution.
getColumnIndex(tBody,columnName) function works fine.
as if it matches this if condition
if(cells[i].childNodes[0].innerHTML.replace(/(\r\n|\n|\r)/gm ,"").trim() == columnName)
{
return i;
}
so that it returns something.
but when you replace this
var index=getColumnIndex(tBody,"Name"); so that coulmnName would be "Name" in String.
And it doesn't match with any columnName so that your condition going to be wrong and function doesn't return anything.
var index=getColumnIndex(tBody,"Name"); the index value is "undefine".
suggestion is put some else condition on that and return some error message like this :
if(cells[i].childNodes[0].innerHTML.replace(/(\r\n|\n|\r)/gm ,"").trim() == columnName)
{
return i;
} else{
// put some error message
// return null
}
i had debug this code with firebug & calling getColumnIndex(tBody,columnName) function works fine
From this, I'm assuming that there isn't anything wrong with the implementation of your getColumnIndex function, so your issue with getting an undefined value must have to do with when this function is returning a value.
but when it return to caller the var index=getColumnIndex(tBody,"Name"); the index value is "undefine".
This leads me to assume that your tBody variable is not being set correctly, given that the "function works fine".
I'm assuming there is a case in your code where the conditions of your getColumnIndex function is not met.
function getColumnIndex(tBody,columnName)
{
var cells = tBody.parentNode.getElementsByTagName('th');
for (var i=0;i<cells.length; i++)
{
if(cells[i].hasChildNodes())
{
if(cells[i].childNodes[0].innerHTML.replace(/(\r\n|\n|\r)/gm ,"").trim() == columnName)
{
return i;
}
}
}
// If your code reaches this point, then the prior conditions have not been met
// You can choose to do something else here for return false/undefined etc.
return undefined;
}
I am currently having some issues with the innerHTML function in a little javascript project. Essentially, I have a few HTML form checkboxes which change a number (that is displayed on the same page) depending on whether they are checked or not. The idea is very much like an IP address. The result is a number between 0 and 255.
What I want to do however is that whenever the user clicks on a checkbox, I need that number to change dynamically. Idea resembles the concept that is used when we write a question on this forum. As you type, the text below changes dynamilly to show exactly what is changed as it changes.
My code isn't working too well. Could you help me please? It keeps giving me the message "undefined" instead of the sum. Thanks for your help.
JavaScript
function displayOctets01(){
var octet01 = new Array(8);
octet01[0] = document.getElementById('octect0101');
octet01[1] = document.getElementById('octect0102');
octet01[2] = document.getElementById('octect0103');
octet01[3] = document.getElementById('octect0104');
octet01[4] = document.getElementById('octect0105');
octet01[5] = document.getElementById('octect0106');
octet01[6] = document.getElementById('octect0107');
octet01[7] = document.getElementById('octect0108');
var firstOctect;
if(octet01[0]==true){
firstOctect+=1;
}
else if(octet01[1]==true){
firstOctect+=2;
}
else if(octet01[2]==true){
firstOctect+=4;
}
else if(octet01[3]==true){
firstOctect+=8;
}
else if(octet01[4]==true){
firstOctect+=16;
}
else if(octet01[5]==true){
firstOctect+=32;
}
else if(octet01[6]==true){
firstOctect+=64;
}
else if(octet01[7]==true){
firstOctect+=128;
}
document.getElementById("octets01").innerHTML = firstOctect;
}
else if(octet01[7]==true){
firstOctect+=128;
}
document.getElementById("octets01").innerHTML = firstOctect;
}
I suspect that something might be wron with how I am handling the variables.
DEMO: http://jsfiddle.net/3TyV3/
The first problem is that the firstOctet variable isn't initialized. That needs to be set to 0 at the beginning of your function. Also, without knowing the purpose of your program, it seems that you don't want to be using else if - you need to check every checkbox. Also, you shouldn't be comparing the element with == true, you should check its checked property Also, your jsFiddle was set to run onLoad, so the function wasn't globally available. Finally, you didn't have an element with the id "octets01" to output to. Try this:
function displayOctets01() {
var octet01 = [],
firstOctect = 0;
octet01[0] = document.getElementById('octect0101');
octet01[1] = document.getElementById('octect0102');
octet01[2] = document.getElementById('octect0103');
octet01[3] = document.getElementById('octect0104');
octet01[4] = document.getElementById('octect0105');
octet01[5] = document.getElementById('octect0106');
octet01[6] = document.getElementById('octect0107');
octet01[7] = document.getElementById('octect0108');
if (octet01[0].checked === true) {
firstOctect += 1;
}
if (octet01[1].checked === true) {
firstOctect += 2;
}
if (octet01[2].checked === true) {
firstOctect += 4;
}
if (octet01[3].checked === true) {
firstOctect += 8;
}
if (octet01[4].checked === true) {
firstOctect += 16;
}
if (octet01[5].checked === true) {
firstOctect += 32;
}
if (octet01[6].checked === true) {
firstOctect += 64;
}
if (octet01[7].checked === true) {
firstOctect += 128;
}
document.getElementById("octets01").innerHTML = firstOctect;
}
DEMO: http://jsfiddle.net/3TyV3/2/
Although I won't lie, I'd reorganize some things. Here's how I would do it:
window.onload = function () {
var checkboxes = document.querySelectorAll('[name="featuresOctet01"]'),
i;
for (i = 0; i < checkboxes.length; i++) {
addEvent(checkboxes[i], "click", clickHandler);
}
};
function addEvent(element, eventName, callback) {
if (element.addEventListener) {
element.addEventListener(eventName, callback, false);
} else if (element.attachEvent) {
element.attachEvent("on" + eventName, callback);
} else {
element["on" + eventName] = callback;
}
}
function clickHandler() {
var firstOctect = 0,
checkboxes = document.querySelectorAll('[name="featuresOctet01"]'),
i, cur;
for (i = 0; i < checkboxes.length; i++) {
cur = checkboxes[i];
if (cur.checked) {
firstOctect += Math.pow(2, i);
}
}
document.getElementById("octets01").innerHTML = firstOctect;
}
DEMO: http://jsfiddle.net/3TyV3/3/
It uses unobtrusive JavaScript by binding the events in JavaScript, not the inline HTML. I did use the click event instead of change because old versions of IE has weird behavior for it with checkboxes/radio buttons. The addEvent function is just a simple function for binding events in new browsers as well as old IE.
It selects all elements with the name "featuresOctet01" and adds the event to each. Then, in the handler, it loops through each checkbox, sees if it's checked, and then adds a value based on 2^i.
References:
addEventListener: https://developer.mozilla.org/en-US/docs/DOM/EventTarget.addEventListener
document.querySelectorAll: https://developer.mozilla.org/en-US/docs/DOM/Document.querySelectorAll
Math.pow: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/pow
I've got this working on mobile devices, but because of the 32kb gzip-ed of jQuery I wonder if it's possible to create this code
$(document).ready(function() {
$('body').addClass('js');
var $menu = $('#menu'),
$menulink = $('.menu-link'),
$wrap = $('#wrap');
$menulink.click(function() {
$menulink.toggleClass('active');
$wrap.toggleClass('active');
return false;
});
});
can be written in no library dependany vanilla JavaScript.
Can it be done? Where would I start?
JQuery uses javascript/DOMscripting to create its framework. Everything JQuery does, can be done in basic scripting. For example $('body').addClass('js') can be written as:
document.querySelector('body').className += ' js';
And $menulink.toggleClass('active'); as something like
var current = $menulink.className.split(/\s+/)
,toggleClass = 'active'
,exist = ~current.indexOf(toggleClass)
;
current.splice(exist ? current.indexOf(toggleClass) : 0,
exist ? 1 : 0,
exist ? null : toggleClass);
$menulink.className = current.join(' ').replace(/^\s+|\s+$/,'');
That's why JQuery wrapped this kind of code.
This jsfiddle contains a working example using javascript without a framework. Besides that it demonstrates how to program your own element wrapper.
Where to start? You'll have to dive into javascript I suppose. Or check this SO-question
For modern browsers only.®
document.addEventListener('DOMContentLoaded', function() {
document.body.classList.add('js');
var wrap = document.getElementById('wrap');
var menuLinks = Array.prototype.slice.call(document.getElementsByClassName('menu-link'));
var toggleActive = function(element) {
element.classList.toggle('active');
};
menuLinks.forEach(function(menuLink) {
menuLink.addEventListener('click', function(e) {
menuLinks.forEach(toggleActive);
toggleActive(wrap);
}, false);
});
}, false);
var toggleClass = function (el, className) {
if(el) {
if(el.className.indexOf(className)) {
el.className = el.className.replace(className, '');
}
else {
el.className += ' ' + className;
}
}
};
document.addEventListener('DOMContentLoaded', function () {
document.body.className += ' js';
var $menu = document.querySelector('#menu'),
$menulink = document.querySelectorAll('.menu-link'),
$wrap = document.querySelector('#wrap');
$menulink.addEventListener('click', function (e) {
toggleClass($menulink, 'active');
toggleClass($wrap, 'active');
e.preventDefault();
});
});
There's always classList (workaround for incompatible browsers included).
Absolutely. Since jQuery is a subset of JavaScript (written entirely in JavaScript) any function you like can be duplicated. It's a matter of how much effort you want to put into it. Below is how I would duplicate the limited subset of jQuery in your post and it's reasonably cross-browser compatible (if a wee bit long...).
var Vanilla;
if (!Vanilla) {
Vanilla = {};
}
//execute this now to have access to it immediately.
(function () {
'use strict';
Vanilla.addHandler = function (elem, event, handler) {
if (elem.addEventListener) {
elem.addEventListener(event, handler, false);
} else if (elem.attachEvent) {
elem.attachEvent('on' + event, handler);
}
};
Vanilla.hasClass = function (elem, cssClass) {
var classExists = false;
//
if (elem && typeof elem.className === 'string' && (/\S+/g).test(cssClass)) {
classExists = elem.className.indexOf(cssClass) > -1;
}
//
return classExists;
};
Vanilla.addClass = function (elem, cssClass) {
if (elem && typeof elem.className === 'string' && (/\S+/g).test(cssClass)) {
//put spaces on either side of the new class to ensure boundaries are always available
elem.className += ' ' + cssClass + ' ';
}
};
Vanilla.removeClass = function (elem, cssClass) {
if (elem && typeof elem.className === 'string'&& (/\S+/g).test(cssClass)) {
//replace the string with regex
cssClass = new RegExp('\\b' + cssClass + '\\b', 'g');
elem.className = elem.className.replace(cssClass, '').replace(/^\s+/g, '').replace(/\s+$/g, ''); //trim className
}
};
Vanilla.toggleClass = function (elem, cssClass) {
if (Vanilla.hasClass(elem, cssClass)) {
Vanilla.removeClass(elem, cssClass);
} else {
Vanilla.addClass(elem, cssClass);
}
};
Vanilla.getElementsByClassName = function (cssClass) {
var nodeList = [],
classList = [],
allNodes = null,
i = 0,
j = 0;
if (document.getElementsByClassName1) {
//native method exists in browser.
nodeList = document.getElementsByClassName(cssClass);
} else {
//need a custom function
classList = cssClass.split(' ');
allNodes = document.getElementsByTagName('*');
for (i = 0; i < allNodes.length; i += 1) {
for (j = 0; j < classList.length; j += 1) {
if (Vanilla.hasClass(allNodes[i], classList[j])) {
nodeList.push(allNodes[i]);
}
}
}
}
return nodeList;
};
}());
//Now we have a proper window onload
Vanilla.addHandler(window, 'load', function () {
'use strict';
var body = document.body,
menu = document.getElementById('menu'),
menulink = [],
wrap = document.getElementById('wrap'),
i = 0,
menulinkClickHandler = function (e) {
var i = 0;
for (i = 0; i < menulink.length; i += 1) {
Vanilla.toggleClass(menulink[i], 'active');
}
Vanilla.toggleClass(wrap, 'active');
return false;
};
Vanilla.addClass(body, 'js');
menulink = Vanilla.getElementsByClassName('menu-link');
for (i = 0; i < menulink.length; i += 1) {
Vanilla.addHandler(menulink[i], 'click', menulinkClickHandler);
}
});
I have this code, that doesn't work in IE 9.
var popup = window.open(url, 'categories','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,copyhistory=no,width=400,height=450,screenX=150,screenY=150,top=150,left=150');
popup.onDone = function(){
var selected = popup.getSelectedIds();
var allready = [];
$("#tab-categories tr input:hidden").each(function(){
if ($.inArray(this.value, selected) != -1) {
allready[allready.length] = this.value;
} else {
$(this).parent().parent().remove();
}
});
for (var i=0, len = selected.length; i<len; i++) {
if ($.inArray(selected[i], allready) != -1) continue;
addCategory(popup.getItemInfo(selected[i]));
}
updateCategoriesCounter();
updateCategoriesMainFlag();
};
Exactly function onDone using as callback function in popup and calls like this:
$("#done").click(function(){
if (window.onDone) window.onDone();
window.close();
return false;
});
but in IE 9 this function looks like undefined.