I'm building a website, primarily for mobiles. I had the following jQuery code
$(".reg_action").click(function () {
var action = $(this).attr("id");
var ele = $(".reg_line.selected");
var icon = $(this).html();
if (action == "deepsleep") {
var color = "#33bb45";
} else if (action == "sleep") {
var color = "#99ef96";
} else if (action == "awake") {
var color = "#e1f648";
} else if (action == "up") {
var color = "#fb0707";
}
ele.find(".reg_segment").val(action);
ele.find(".reg_color").css("background-color", color);
ele.find(".reg_icon").html(icon);
// Move on
ele.removeClass("selected");
ele.next().addClass("selected");
})
I know it might not be the best way all of it, but anyways it is EXTREMELY slow on iPhones - not fully tested, but seems like it is a general problem, even on the newer. I tried making it in JS indstead (again, might not be perfect):
function lineAction(action) {
if (action == "deepsleep") {
var color = "#33bb45";
} else if (action == "sleep") {
var color = "#99ef96";
} else if (action == "awake") {
var color = "#e1f648";
} else if (action == "up") {
var color = "#fb0707";
}
var ele = document.getElementsByClassName("selected");
ele[0].childNodes[1].value = action;
ele[0].childNodes[3].style.backgroundColor = color;
var classes = document.getElementsByClassName("selected");
classes[0].nextSibling.classList.add("selected");
classes[0].className = classes[0].className.replace(/\bselected\b/, '');
}
But even that does not seem to help. Any suggestions how to speed this up a lot? I've been googling, and it seems like DOM manipulation is just slow on iPhone. Is there a solution?
Would it for example help to make the 5 states of each line (default, deepsleep, sleep, awake, up) and then just hide/show the one needed? Pageload is not an issue at all.
Use object with keys to match and the corresponding value to be set as the value of the key. Then the value can be accessed by using the key. e.g. color[action]
Reuse the cached reference of the DOM element
Use remove method of classList to remove a class from a element, no need of ragex here
Instead of using the click event, use touchstart event
Here is the VanillaJS updated code.
var color = {
'deepsleep': '#33bb45',
'sleep': '#99ef96',
'awake': '#e1f648',
'up': '#fb0707
};
function lineAction(action) {
var ele = document.getElementsByClassName("selected");
ele[0].childNodes[1].value = action;
ele[0].childNodes[3].style.backgroundColor = color[action];
ele[0].nextSibling.classList.add("selected");
ele[0].className.remove('selected');
}
Related
I've been playing with javascript to create a drop down list that shows a div depending on which option is selected.
All the code can be seen here:
http://jsfiddle.net/nmdTy/
var select = document.getElementById('test'),
onChange = function(event) {
var shown = this.options[this.selectedIndex].value == 1;
document.getElementById('hidden_div').style.display = shown ? 'block' : 'none';
};
I want to know how do I streamline this code and remove repetition - maybe some kind of loop?
Another code :
var select = document.getElementById('test'),
nbItems = 2,
onChange = function (event) {
var val = this.options[this.selectedIndex].value;
for (var i = 1; i <= nbItems; i++) {
document.getElementById('hidden_div' + i).style.display = val == i ? 'block' : 'none';
}
};
http://jsfiddle.net/nmdTy/11/
You don't need two event handlers, you can use variables (shown below) to determine which div needs to be displayed or hidden.
var select = document.getElementById('test'), onChange = function(event) {
var div1 = 'hidden_div';
var div2 = 'hidden_div2';
var index1 = this.options[this.selectedIndex].value == 1;
var index2 = this.options[this.selectedIndex].value == 2;
if(index1 || index2){
document.getElementById(div1).style.display = index1 ? 'block' : 'none';
document.getElementById(div2).style.display = index2 ? 'block' : 'none';
}
else{
document.getElementById(div1).style.display = 'none';
document.getElementById(div2).style.display = 'none';
}
};
// attach event handler
if (window.addEventListener) {
select.addEventListener('change', onChange, false);
} else {
// of course, IE < 9 needs special treatment
select.attachEvent('onchange', function() {
onChange.apply(select, arguments);
});
}
Working Fiddle
I'm not really sure what do you mean by "repetition" but my guess is, that you don't want to type every each of the divs to be hidden/shown.
There could be multiple approaches to such task. The most universal is to have the div id's in a separate array. Then you can hide all but the selected div.
var divs = ["hidden_div1", "special_hidden", "one_more_hidden"];
var select = document.getElementById('test');
var onchange = function(event) { //Use var!
var shown = this.options[this.selectedIndex].value;
for(var i=0; i<window.divs.length; i++) { //It would be more effective to save last shown div in a variable, but I've chosen this aproach with loop
var div = document.getElementById(window.divs[i]);
if(div!=null) {
if(i==shown)
div.style.display="block";
else
div.style.display="none";
}
}
};
select.addEventListener("change", onchange); //Could type the function right here, without using "onchange" variable
In my code, <option> value represents index in the array. Here is jsFiddle.
Delegating a change event in IE<9 is a pain. It is possible, check this question to see how it's done, but it's not what you call elegant.
But your code doesn't delegate the event, so just attaching the handler directly at the onload event should do the trick (and it's X-browser compatible):
document.getElementById('test').onchange = function(e)
{
e = e || window.event;//the only IE headache
var shown = this.options[this.selectedIndex].value == 1;
document.getElementById('hidden_div').style.display = shown ? 'block' : 'none';
//^^ could keep a reference to this in a closure
};
The full code (with onload and closure reference to hidden div and preventing memory leaks in ie) should look like this:
var winLoad = function(e)
{
var hiddenDiv = document.getElementById('hidden_div');
document.getElementById('test').onchange = function(e)
{
var shown = !!(this.option[this.selectedIndex].value == 1);//to be safe, coerce to bool
hiddenDiv.style.display = shown ? 'block' : 'none';
};
if (window.addEventListener)
{
return window.removeEventListener('load',winLoad,false);
}
return window.detachEvent('onload',winLoad);
};
if (window.addEventListener)
{
window.addEventListener('load',winLoad,false);
}
else
{
window.attachEvent('onload',winLoad);
}
that should work fine on all major browsers, even IE7 (probably IE6, too)
I am using this little script to toggle classes of an element on click of another element. Here is the stripped down code:
//Toggle comments
function togglecomments() {
function shiftcomments() {
var comments = document.getElementsByTagName('aside')[0];
if(comments.className = "hide"){comments.className = "show";}
else{comments.className = "hide";};
}
var commenttoggle = document.getElementById('toggle-comments');
bindEvt(commenttoggle, "click", shiftcomments);
}
bindEvt(window, "load", togglecomments);
The thing is it works once, but after that on click the class does not toggle anymore. For those interested here is the event handler I use: http://pastebin.com/md3dPvMJ (It worked fine before so it shouldn't be the problem.)
Any ideas what I did wrong?
Thanks for your feedback guys!
In your if statements you've got this:
if(comments.className = "hide")
It should be:
if(comments.className === "hide")
This would also work:
if(comments.className == "hide")
What you are actually doing up there is changing the className to "hide", not checking for equality.
For the difference between == and === I'll actually point you to another question here at stackoverflow: Which equals operator (== vs ===) should be used in JavaScript comparisons?
This is a little function I'm using, with jQuery, but it can be updated.
toggleClass = function (obj, className) {
var currentClass = $(obj).attr('class'),
hasIt = false;
if (currentClass.indexOf(className) < 0) {
currentClass += " " + className;
hasIt = true;
} else {
currentClass = currentClass.replace(className, "");
hasIt = false;
}
$(obj).attr('class', currentClass);
return hasIt;
};
I'm currently working on a Bookmarklet for Maximo, which is a Java EE application, and I need to populate a few input boxes.
Generally when a use inputs data into the box they click a button that gives them a popup and they search for the value to be added to the script. Or they can type the name and hit tab/enter and it turns it to capital letters and does a few things in the background (not sure what it does exactly).
I currently use
Javascript: $('mx1354').value = "KHBRARR"; $('mx1354').ov= "KHBRARR";
But it does not work like I need it to. It set's the input box to the value needed, but it doesn't run the background functions so when I hit the save button it doesn't recognize it as any changes and discards what I put into the box.
How could I simulate a tab/enter button has been pressed?
So far I've tried to call the onchange, focus/blur, and click functions (Not 100% sure if I called them correctly).
The dojo library is part of the application, so I'm not sure if I can use one if it's feature or if jQuery would cause a conflict.
P.S. This needs to run in IE.
The OnChange Function:
function tb_(event)
{
event = (event) ? event : ((window.event) ? window.event : "");
if(DESIGNMODE)
return;
var ro = this.readOnly;
var exc=(this.getAttribute("exc")=="1");
switch(event.type)
{
case "mousedown":
if(getFocusId()==this.id)
this.setAttribute("stoptcclick","true");
break;
case "mouseup":
if (isIE() && !hasFocus(this))
{
this.focus();
}
if (isBidiEnabled)
{
adjustCaret(event, this); // bidi-hcg-AS
}
break;
case "blur":
input_onblur(event,this);
if (isBidiEnabled) // bidi-hcg-SC
input_bidi_onblur(event, this);
break;
case "change":
if(!ro)
input_changed(event,this);
break;
case "click":
if(overError(event,this))
showFieldError(event,this,true);
var liclick=this.getAttribute("liclick");
var li=this.getAttribute("li");
if(li!="" && liclick=="1")
{
frontEndEvent(getElement(li),'click');
}
if(this.getAttribute("stoptcclick")=="true")
{
event.cancelBubble=true;
}
this.setAttribute("stoptcclick","false");
break;
case "focus":
input_onfocus(event,this);
if (isBidiEnabled) // bidi-hcg-SC
input_bidi_onfocus(event, this);
this.select();
break;
case "keydown":
this.setAttribute("keydown","true");
if(!ro)
{
if(isBidiEnabled)
processBackspaceDelete(event,this); // bidi-hcg-AS
if(hasKeyCode(event, 'KEYCODE_DELETE') || hasKeyCode(event, 'KEYCODE_BACKSPACE'))
{
getHiddenForm().elements.namedItem("changedcomponentvalue").value = this.value;
}
if((hasKeyCode(event, 'KEYCODE_TAB') || hasKeyCode(event, 'KEYCODE_ESC')))
{
var taMatch = dojo.attr(this, "ta_match");
if(taMatch) {
if(taMatch.toLowerCase().indexOf(this.value.toLowerCase()) == 0)
{
console.log("tamatch="+taMatch);
this.value = taMatch;
input_keydown(event, this);
dojo.attr(this, {"prekeyvalue" : ""});
input_forceChanged(this);
inputchanged = false;
return; // don't want to do input_keydown again so preKeyValue will work
}
}
if(this.getAttribute("PopupType"))
{
var popup = dijit.byId(dojohelper.getPopupId(this));
if (popup)
{
dojohelper.closePickerPopup(popup);
if(hasKeyCode(event, 'KEYCODE_ESC'))
{
if (event.preventDefault)
{
event.preventDefault();
}
else
{
event.returnValue = false;
}
return;
}
}
}
}
input_keydown(event,this);
datespin(event,this);
}
else if(hasKeyCode(event,'KEYCODE_ENTER') || (hasKeyCode(event,'KEYCODE_DOWN_ARROW') && this.getAttribute("liclick")))
{
var lbId = this.getAttribute("li");
frontEndEvent(getElement(lbId), 'click');
}
else if(hasKeyCode(event,KEYCODE_BACKSPACE))
{
event.cancelBubble=true;
event.returnValue=false;
}
break;
case "keypress":
if(!ro)
{
if(event.ctrlKey==false && hasKeyCode(event,'KEYCODE_ENTER'))
{
var db = this.getAttribute("db");
if(db!="")
{
sendClick(db);
}
}
}
break;
case "keyup":
var keyDown = this.getAttribute("keydown");
this.setAttribute("keydown","false");
if(event.ctrlKey && hasKeyCode(event,'KEYCODE_SPACEBAR'))
{
if(showFieldError(event,this,true))
{
return;
}
else
{
menus.typeAhead(this,0);
}
}
if(!ro)
{
if(isBidiEnabled)
processBidiKeys(event,this); // bidi-hcg-AS
numericcheck(event,this);
var min = this.getAttribute("min");
var max = this.getAttribute("max");
if(min && max && min!="NONE" || max!="NONE")
{
if(min!="NONE" && parseInt(this.value)<parseInt(min))
{
this.value=min;
getHiddenForm().elements.namedItem("changedcomponentvalue").value = this.value;
this.select();
return false;
}
if(max!="NONE" && parseInt(this.value)>parseInt(max))
{
this.value=max;
getHiddenForm().elements.namedItem("changedcomponentvalue").value = this.value;
this.select();
return false;
}
}
var defaultButton = false;
if(event.ctrlKey==false && hasKeyCode(event,'KEYCODE_ENTER'))
{
var db = this.getAttribute("db");
if(db!="")
{
defaultButton=true;
}
}
input_changed(event,this);
}
else
{
setFocusId(event,this);
}
if(showFieldHelp(event, this))
{
return;
}
if(keyDown=="true" && hasKeyCode(event, 'KEYCODE_ENTER') && !event.ctrlKey && !event.altKey)
{
menus.typeAhead(this,0);
return;
}
if(!hasKeyCode(event, 'KEYCODE_ENTER|KEYCODE_SHIFT|KEYCODE_CTRL|KEYCODE_ESC|KEYCODE_ALT|KEYCODE_TAB|KEYCODE_END|KEYCODE_HOME|KEYCODE_RIGHT_ARROW|KEYCODE_LEFT_ARROW')
&& !event.ctrlKey && !event.altKey)
{
menus.typeAhead(this,0);
}
break;
case "mousemove":
overError(event,this);
break;
case "cut":
case "paste":
if(!ro)
{
var fldInfo = this.getAttribute("fldInfo");
if(fldInfo)
{
fldInfo = dojo.fromJson(fldInfo);
if(!fldInfo.query || fldInfo.query!=true)
{
setButtonEnabled(saveButton,true);
}
}
window.setTimeout("inputchanged=true;input_forceChanged(dojo.byId('"+this.id+"'));", 20);
}
break;
}
}
After some time I found that in order to make a change to the page via JavaScript you need to submit a hidden form so it can verify on the back-end.
Here is the code I used to change the value of Input fields.
cc : function(e,v){
e.focus(); //Get focus of the element
e.value = v; //Change the value
e.onchange(); //Call the onchange event
e.blur(); //Unfocus the element
console.log("TITLE === "+e.title);
if(e.title.indexOf(v) != -1) {
return true; //The value partially matches the requested value. No need to update
} else {
//Generate an hidden form and submit it to update the page with the new value
var hiddenForm = getHiddenForm();
var inputs = hiddenForm.elements;
inputs.namedItem("changedcomponentid").value = e.id;
inputs.namedItem("changedcomponentvalue").value = v;
inputs.namedItem("event").value = "X"; //Send a Dummy Event so the script see's its invalid and sets the right Event
submitHidden();
}
//Value isn't set to the required value so pass false
return false;
}
run this
input_changed(null,document.getElementById('IDHERE'));
In maximo 7.5 i built a custom lookup
when i click the colored hyperlink java script is called to update the values back to parent form values or updated but on save the value or not updated
function riskmatrix_setvalue(callerId, lookupId, value,bgrColor,targetid){
if (document.getElementById(callerId).readOnly){
sendEvent('selectrecord', lookupId);
return;
}
textBoxCaller = document.getElementById(callerId);
//dojo.byId(callerId).setAttribute("value", value);
//dojo.byId(callerId).setAttribute("changed", true);
//dojohelper.input_changed_value(dojo.byId(callerId),value);
//textBoxCaller.style.background = bgrColor;
//var hiddenForm = getHiddenForm();
//if(!hiddenForm)
// return;
//var inputs = hiddenForm.elements;
//inputs.namedItem("event").value = "setvalue";
//inputs.namedItem("targetid").value = dojo.byId(callerId).id;
//inputs.namedItem("value").value = value;
//sendXHRFromHiddenForm();
textBoxCaller.focus(); //Get focus of the element
textBoxCaller.value = value; //Change the value
textBoxCaller.onchange(); //Call the onchange event
textBoxCaller.blur(); //Unfocus the element
//Generate an hidden form and submit it to update the page with the new value
var hiddenForm = getHiddenForm();
var inputs = hiddenForm.elements;
inputs.namedItem("changedcomponentid").value = textBoxCaller.id;
inputs.namedItem("changedcomponentvalue").value = value;
inputs.namedItem("event").value = "X"; //Send a Dummy Event so the script see's its invalid and sets the right Event
submitHidden();
sendEvent("dialogclose",lookupId);
}
Description
I changed a bit #Steven10172's perfect solution and made it into a Javascript re-usable function.
Made this into a separate answer since my edits to the original answer where i added this were refused :)
I also had to change the line e.onchange() to e.onchange(e) because otherwise the textbox handler (tb_(eventOrComponent) function) would throw TypeError: textbox.getAttribute is not a function.
Code
var setFakeValue = function(e,v){
console.log("Changing value for element:", e, "\nNew value:", v);
e.focus(); //Get focus of the element
e.value = v; //Change the value
e.onchange(e); //Call the onchange event
e.blur(); //Unfocus the element
if(e.title.indexOf(v) != -1) {
return true; //The value partially matches the requested value. No need to update
}
else {
//Generate an hidden form and submit it to update the page with the new value
var hiddenForm = getHiddenForm();
var inputs = hiddenForm.elements;
inputs.namedItem("changedcomponentid").value = e.id;
inputs.namedItem("changedcomponentvalue").value = v;
inputs.namedItem("event").value = "X"; //Send a Dummy Event so the script see's its invalid and sets the right Event
submitHidden();
}
//Value isn't set to the required value so pass false
return false;
}
Usage
setFakeValue(html_element, new_value);
Fun fact
I spent a lot of time searching for a solution to programmatically change an <input> value in Maximo... At some point i got really frustrated, gave up and started to think it just wasn't possible...
Some time ago i tried to search with no expectations at all and after some time i found the solution... Here...
Now... As you can see this is literally just a total copy of StackOverflow, including questions and solutions (marking the upvotes with plain text lol), but in Chinese... This got me curious and after a little search i found this post on StackOverflow..
High five to Chrome built-in webpage translator that let understand something on that page ^^
Problem statement:
It is necessary for me to write a code, whether which before form sending will check all necessary fields are filled. If not all fields are filled, it is necessary to allocate with their red colour and not to send the form.
Now the code exists in such kind:
function formsubmit(formName, reqFieldArr){
var curForm = new formObj(formName, reqFieldArr);
if(curForm.valid)
curForm.send();
else
curForm.paint();
}
function formObj(formName, reqFieldArr){
var filledCount = 0;
var fieldArr = new Array();
for(i=reqFieldArr.length-1; i>=0; i--){
fieldArr[i] = new fieldObj(formName, reqFieldArr[i]);
if(fieldArr[i].filled == true)
filledCount++;
}
if(filledCount == fieldArr.length)
this.valid = true;
else
this.valid = false;
this.paint = function(){
for(i=fieldArr.length-1; i>=0; i--){
if(fieldArr[i].filled == false)
fieldArr[i].paintInRed();
else
fieldArr[i].unPaintInRed();
}
}
this.send = function(){
document.forms[formName].submit();
}
}
function fieldObj(formName, fName){
var curField = document.forms[formName].elements[fName];
if(curField.value != '')
this.filled = true;
else
this.filled = false;
this.paintInRed = function(){
curField.addClassName('red');
}
this.unPaintInRed = function(){
curField.removeClassName('red');
}
}
Function is caused in such a way:
<input type="button" onClick="formsubmit('orderform', ['name', 'post', 'payer', 'recipient', 'good'])" value="send" />
Now the code works. But I would like to add "dynamism" in it.
That it is necessary for me: to keep an initial code essentially, to add listening form fields (only necessary for filling).
For example, when the field is allocated by red colour and the user starts it to fill, it should become white.
As a matter of fact I need to add listening of events: onChange, blur for the blank fields of the form. As it to make within the limits of an initial code.
If all my code - full nonsense, let me know about it. As to me it to change using object-oriented the approach.
Give me pure Javascript solution, please. Jquery - great lib, but it does not approach for me.
To keep your HTML clean, I suggest a slightly different strategy.
Use a framework like jQuery which makes a lot of things much more simple.
Move all the code into an external script.
Use the body.onLoad event to look up all forms and install the checking code.
Instead of hardcoding the field values, add a css class to each field that is required:
<input type="text" ... class="textField required" ...>
Note that you can have more than a single class.
When the form is submitted, examine all fields and check that all with the class required are non-empty. If they are empty, add the class error otherwise remove this class. Also consider to add a tooltip which says "Field is required" or, even better, add this text next to the field so the user can see with a single glance what is wrong.
In the CSS stylesheet, you can then define a rule how to display errors.
For the rest of the functionaly, check the jQuery docs about form events.
Has made.
function formsubmit(formName, reqFieldArr){
var curForm = new formObj(formName, reqFieldArr);
if(curForm.valid)
curForm.send();
else{
curForm.paint();
curForm.listen();
}
}
function formObj(formName, reqFieldArr){
var filledCount = 0;
var fieldArr = new Array();
for(i=reqFieldArr.length-1; i>=0; i--){
fieldArr[i] = new fieldObj(formName, reqFieldArr[i]);
if(fieldArr[i].filled == true)
filledCount++;
}
if(filledCount == fieldArr.length)
this.valid = true;
else
this.valid = false;
this.paint = function(){
for(i=fieldArr.length-1; i>=0; i--){
if(fieldArr[i].filled == false)
fieldArr[i].paintInRed();
else
fieldArr[i].unPaintInRed();
}
}
this.send = function(){
document.forms[formName].submit();
}
this.listen = function(){
for(i=fieldArr.length-1; i>=0; i--){
fieldArr[i].fieldListen();
}
}
}
function fieldObj(formName, fName){
var curField = document.forms[formName].elements[fName];
this.filled = getValueBool();
this.paintInRed = function(){
curField.addClassName('red');
}
this.unPaintInRed = function(){
curField.removeClassName('red');
}
this.fieldListen = function(){
curField.onkeyup = function(){
if(curField.value != ''){
curField.removeClassName('red');
}
else{
curField.addClassName('red');
}
}
}
function getValueBool(){
if(curField.value != '')
return true;
else
return false;
}
}
This code is not pleasant to me, but it works also I could not improve it without rewriting completely.
jQuery adds a lot of benefit for a low overhead. It has a validation plugin which is very popular. There are some alternatives as well but I have found jQuery to be the best.
Benefits
small download size
built in cross browser support
vibrant plug-in community
improved productivity
improved user experience
I have written a game in java script and while it works, it is slow responding to multiple clicks. Below is a very simplified version of the code that I am using to handle clicks and it is still fails to respond to a second click of 2 if you don't wait long enough. Is this something that I need to just accept or is there a faster way to be ready for the next click?
BTW, I attach this function using AddEvent from the quirksmode recoding contest.
var selected = false;
var z = null;
function handleClicks(evt) {
evt = (evt)?evt:((window.event)?window.event:null);
if (selected) {
z.innerHTML = '<div class="rowbox a">a</div>';
selected = false;
} else {
z.innerHTML = '<div class="rowbox selecteda">a</div>';
selected = true;
}
}
The live code may be seen at http://www.omega-link.com/index.php?content=testgame
You could try to only change the classname instead of removing/adding a div to the DOM (which is what the innerHTML property does).
Something like:
var selected = false;
var z = null;
function handleClicks(evt)
{
var tmp;
if(z == null)
return;
evt = (evt)?evt:((window.event)?window.event:null);
tmp = z.firstChild;
while((tmp != null) && (tmp.tagName != 'DIV'))
tmp = tmp.firstChild;
if(tmp != null)
{
if (selected)
{
tmp.className = "rowbox a";
selected = false;
} else
{
tmp.className = "rowbox selecteda";
selected = true;
}
}
}
I think your problem is that the 2nd click is registering as a dblclick event, not as a click event. The change is happening quickly, but the 2nd click is ignored unless you wait. I would suggest changing to either the mousedown or mouseup event.
I believe your problem is the changing of the innerHTML which changes the DOM which is a huge performance problem.
Yeah you may want to compare the performance of innerHTML against document.createElement() or even:
el.style.display = 'block' // turn off display: none.
Profiling your code may be helpful as you A/B various refactorings:
http://www.mozilla.org/performance/jsprofiler.html
http://developer.yahoo.com/yui/profiler/
http://weblogs.asp.net/stevewellens/archive/2009/03/26/ie-8-can-profile-javascript.aspx