1. Advertising
    y u no do it?

    Advertising (learn more)

    Advertise virtually anything here, with CPM banner ads, CPM email ads and CPC contextual links. You can target relevant areas of the site and show ads based on geographical location of the user if you wish.

    Starts at just $1 per CPM or $0.10 per CPC.

JavaScript Post Request

Discussion in 'JavaScript' started by KewL, Nov 11, 2015.

  1. #1
    Hey guys, I have a simple static site I'm trying to add a contact form to. I made a simple mailer app thing (http://mailer.robertklein.co) that accepts JSON like this:

    {
      "name": "Robert",
      "email": "email@example.com",
      "message": "blah blah blah"
    }
    Code (markup):
    I can't figure out how to make this request using plain JS. Can someone please show me how its done and break it down for me? I'm having trouble finding a full example online that uses plain js and json.

    Really appreciate it, thanks!
     
    KewL, Nov 11, 2015 IP
  2. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,998
    Best Answers:
    253
    Trophy Points:
    515
    #2
    I'm going to assume you mean using AJAX.

    The key to this is that even "POST" methods URIEncode the values and sends them in the same format as $_GET, they just do so in the headers instead of the URI.

    function serializeObjectToURI(o) {
    	var result = [];
    	for (var i in o) result.push(
    		encodeURIComponent(i) + '=' + uncodeURIComponent(o[i])
    	);
    	return result.join('&');
    }
    // assumes x is a XMLHttpRequest object
    
    x.open('POST', 'test.php', true);
    x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    x.send(serializeObjectToURI(yourObjectVariableHere));
    Code (markup):
    Basically serializeObjectToURI would turn your JSON above into:

    name=Robert&email=email%40example.com&message=blah%20blah%20blah

    I've heard that JSON.Stringify is somehow capable of this, but I've never gotten it to work and the above functions in older browsers that don't have the JSON object, so I do it this way.
     
    deathshadow, Nov 11, 2015 IP
  3. KewL

    KewL Well-Known Member

    Messages:
    245
    Likes Received:
    16
    Best Answers:
    3
    Trophy Points:
    128
    #3
    Ok now I'm confused about why I'm using JSON at all...

    I specifically set up my application to read a JSON object in the request body. Seems like there's no point constructing a JSON object only to decode it and send it in the url.

    Is there any benefit to using JSON directly or even just clientside? Im thinking I'm probably better off doing something like this:

    Edit: see response below...
     
    Last edited: Nov 11, 2015
    KewL, Nov 11, 2015 IP
  4. hdewantara

    hdewantara Well-Known Member

    Messages:
    536
    Likes Received:
    47
    Best Answers:
    25
    Trophy Points:
    155
    #4
    As far as I know,
    there's no native ajax.responseJSON. Don't know why though:) Browsers will only report back an ajax response as plain text string stored in ajax.responseText and as an XML object stored in ajax.responseXML. This is the response part. So what about request to server?

    Well, natively $_GET and $_POST variables are plain text strings too. Not XML, not even JSON objects... right?

    The only reason I know to use JSON is because it requires less space "compared to" XML format. Thus it would be more efficient in transport.
     
    Last edited: Nov 11, 2015
    hdewantara, Nov 11, 2015 IP
  5. KewL

    KewL Well-Known Member

    Messages:
    245
    Likes Received:
    16
    Best Answers:
    3
    Trophy Points:
    128
    #5
    Yeah, the whole JSON things seeming kind of pointless now.

    Ok here's what I ended up doing, you guys tell me if you see anything wrong, or somewhere that can be improved.

    I dropped the whole JSON thing server-side and now have it send back a plain text response.

    As far as the client-side goes I did this:

    var submit = document.getElementById('submit')
    
    submit.onclick = function(event) {
      event.preventDefault()
    
      var name = document.getElementById('name')
      , email = document.getElementById('email')
      , message = document.getElementById('message')
      , response = document.getElementById('form-response')
      , fields = document.getElementsByClassName('input-field')
      , x = new XMLHttpRequest()
      , encoded = encodeURI('name=' + name.value + '&email=' + email.value + '&message=' + message.value)
    
      x.open('POST', 'http://localhost:3002', true)
      x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
      x.send(encoded)
    
      x.onreadystatechange = function() {
        if (this.readyState === 4) {
          if (this.status >= 200 && this.status < 400) {
         
            response.innerHTML = this.responseText
    
            for (var i = 0; i < fields.length; i++) {
              fields[i].value = ''
            }
    
          } else {
            response.innerHTML = "Something went wrong..."
          }
        }
      }
    }
    Code (JavaScript):
     
    KewL, Nov 11, 2015 IP
  6. hdewantara

    hdewantara Well-Known Member

    Messages:
    536
    Likes Received:
    47
    Best Answers:
    25
    Trophy Points:
    155
    #6
    Line #20 looks uncommon. Usually, it looks like:
    if (this.status === 200){}
    Code (JavaScript):
    Is it because test is conducted on localhost ?

    Untested, but if you have a form like:
    <form class="myForm">
        <input name="username" type="text">
        <select name="userselect">
            <option value="1">one</option>
            <option value="2">two</option>
        </select>
        <input type="submit">
    </form>
    HTML:
    it's easier I guess to create query with something like:
    <script>
        document.querySelector('.myForm').onsubmit = function(e){
            e.preventDefault();
            var elements = this.querySelectorAll('[name]'), q = [];
            for (var i = 0; i < elements.length; i++)
                q.push(elements[i].name + '=' + encodeURIComponent(elements[i].value));
            q.join('&');
            alert(q);
        };
    </script>
    Code (JavaScript):
     
    hdewantara, Nov 11, 2015 IP
  7. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,998
    Best Answers:
    253
    Trophy Points:
    515
    #7
    BINGO. In terms of sending to the SERVER? useless. The only reason to use JSON is to parse a response FROM the server, NOT for sending it TO the server. (now, if you could actually open up PUSH, but nobody uses that since it's a security hole from hell)

    @hdewantara has it right that your >=200 <400 is just bizarre; usually you don't want to process anything other than a OK. Particularly given the rubbish 300 responses could give you.

    Though, seriously @KewL, what is up with your bizzaroland code formatting? Also I probably would not be using innerHTML, ECMA has only spent fifteen years trying to get us to stop using that. Do a node flush and then an appendChild(document.createTextNode())

    Gah, that document.getElementById over and over and over would drive me nuts, and it's not very portable either. @hdewantara's code is an improvement, but the use of querySelectorAll leaves you a little hobbled on legacy support, and detecting the name attribute probably isn't the best trigger either.

    Also, encodeURI should be encoding those equal signs, breaking it! You can't add it together THEN encode it, the ampersands and equal signs will be then encoded!!! You HAVE to do it one piece at a time, and you HAVE to encode BOTH the name and the value! Likewise you have to set onreadystatechange BEFORE you send, or the response could actually fire BEFORE that loads!

    Those are some pretty big deal-breaking flubs.

    This is untested and may have a few typos, but the overall concept is sound and handles all the litle corner-cases.

    (function(d) {
    
    	// some helper functions, let's make this work all the way back to IE 5.5
    
    	function activeXObj(n) {
    		try { return new ActiveXObject(n); } catch (e) { return false; }
    	}
    
    	function ajaxNew(method, uri, handler) {
    		var x = window.XMLHttpRequest ? new XMLHttpRequest() : (
    			activeXObj('Msxml2.XMLHTTP.6.0') ||
    			activeXObj('Msxml2.XMLHTTP.3.0') ||
    			activeXObj('Microsoft.XMLHTTP')
    		);
    		x.send(method, uri);
    		x.onreadystatechange = handler;
    		return x;
    	}
    	
    	function classExists(e, className) {
    		return RegExp('(\\s|^)' + className + '(\\s|$)').test(e.className);
    	}
    	
    	function eventAdd(e, event, handler) {
    		if (e.addEventListener) e.addEventListener(event, handler, false);
    			else e.attachEvent('on' + event, handler);
    	}
    	
    	function nodeFlush(e) {
    		while (e.firstChild) e.removeChild(e.firstChild);
    	}
    	
    	function nodeAdd(e, newNode) {
    		e.appendChild(
    			(typeof newNode == 'object') ? newNode : d.createTextNode(newNode)
    		);
    	}
    	
    	function nodeReplace(e, newChild) {
    		nodeFlush(e);
    		nodeAdd(e, newChild);
    	}
    	
    	// now for our actual functionality
    	
    	/*
    		It is usually better to trust the object being passed as a parameter
    		than it is to try and use "this" to access it!
    	*/
    	function ajaxHandler(x) {
    		/*
    			I usually have a WAY more robust handler here, as I like to report
    			all the ajax states as they happen so the user has SOME clue that
    			we're actually doing something here!
    		*/
    		if (x.readyState == 4) {
    		
    			var response = document.getElementById('form-response');
    			
    			switch (x.status) {
    			
    				case 200:
    					nodeReplace(response, x.responseText);
    					for (var i = 0; i < x.processForm.elements.length; i++) 
    						x.processForm.elements[i].value = '';
    				break;
    				
    				default:
    					nodeReplace(
    						response,
    						'Something went wrong, HTTP response code ' + x.status
    					);
    					
    			}
    			
    		}
    		
    	} // ajaxHandler
    	
    	function submitHandler(e) {
    	
    		e = e || window.event;
    		var
    			x = ajaxNew('POST', 'http://localhost:3002', ajaxHandler),
    			form = e.target || e.srcElements,
    			data = [];
    			
    		x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    		// plug these into our ajax object 
    		x.processForm = form;
    		
    		// one of the few times I'd use the "outdated" formElement.elements property
    		for (var i = 0; i < form.elements.length; i++)
    			if (form.elements[i].name) data.push(
    				encodeURIComponent(form.elements[i].name) + '=' + 
    				encodeURIComponent(form.elements[i].value)
    			);
    		
    		// always send LAST!!!	
    		x.send(data.join('&'));
    		
    	} // submitHandler
    
    	/*
    		iterate through all forms checking for class since legacy doesn't have
    		getElementsByClassName or querySelector methods.
    	*/
    	var forms = document.getElementsByTagName('form');
    		
    	for (var i = 0; i < forms.length; i++)
    		if (classExists(forms[i], 'ajaxMe'))
    			eventAdd(forms[i], 'submit', submitHandler);
    		
    })(document);
    Code (markup):
    Also works in a isolated scope thanks to the self calling function, so it won't interfere with other scripting. The way I wrote it all you have to do is add the class "ajaxMe" to any form you want to be auto-processed. As mentioned thanks to it's construction methodology it SHOULD work all the way back to IE 5.5, possibly even 5.1 if Excel is installed. (5.1 when Excel '97 is installed gains XMLHttpRequest, that's actually the historical origin of it!).

    I might be tempted to make it even more 'generic' and universal by using the HTML 5 "data" attribute (that can be used back to IE 5.x thanks to IE's sloppy attribute parsing) to set the target of the response text. More robust may even change that response text when the submit starts to say something like "please wait".... maybe even do a "disabled" on all the form elements until the submit is complete.

    If I have time later I'll see about making sure that code actually runs and setup a small demo for it just to be sure.
     
    deathshadow, Nov 11, 2015 IP
  8. KewL

    KewL Well-Known Member

    Messages:
    245
    Likes Received:
    16
    Best Answers:
    3
    Trophy Points:
    128
    #8
    @deathshadow , @hdewantara Thanks for all help! The status code >= stuff was copied from a stackoverflow question.

    @hdewantara , Thanks for the insight on query selector, never seen that before!

    @deathshadow , Thanks for writing that all out man, sounds pretty bullet proof! Will be testing this out in a second. The comma on the next line has kind of grew on me, it's so easy to see where I missed one as apposed to the end of the previous line. My line breaks are all funky, is there any rule of thumb for this? Feeling inspired man, now I'll have to make the whole site ie5.5+ compatible.

    Man I have a long way to go as far as becoming efficient in JS goes. I've never seen any of those node* stuff. All my code is so specific, i need to break that habit of thinking.
     
    KewL, Nov 11, 2015 IP
  9. KewL

    KewL Well-Known Member

    Messages:
    245
    Likes Received:
    16
    Best Answers:
    3
    Trophy Points:
    128
    #9
    @deathshadow, couple of questions about your script.

    1. Nothings stopping the event. I was using preventDefault(), but it looks like thats only ie 9+, how would you handle this?

    2. Once i stop it, I'm getting the following error:
    I there something incorrect here:
    
        var x = window.XMLHttpRequest ? new XMLHttpRequest() : (
          activeXObj('Msxml2.XMLHTTP.6.0') ||
          activeXObj('Msxml2.XMLHTTP.3.0') ||
          activeXObj('Microsoft.XMLHTTP')
        );
    
    Code (markup):
    Edit:

    Ok looks like I can stop the event by throwing 'return false' at the bottom of the submit handler. I can't figure out how to open the request. I tried doing it like the script i posted above, not sure how to do it
     
    Last edited: Nov 11, 2015
    KewL, Nov 11, 2015 IP
  10. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,998
    Best Answers:
    253
    Trophy Points:
    515
    #10
    oops, BIG typo.

    That should be x.open inside that function, NOT x.send.... and yeah, I forgot to add event prevention to it; I have a little helper function for that too.

    If insomnia sets in I'll write a working test, otherwise I'll hit in in the morning.
     
    deathshadow, Nov 11, 2015 IP
  11. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,998
    Best Answers:
    253
    Trophy Points:
    515
    #11
    ooph, forget that above code; *EGG ON FACE* I'm a dumbass... Wow, been so long I forget that 1) the handler is supposed to get the event handed to it, and 2) IE7/earlier don't pass anything NOR do they set window.event since it's an activeX process...

    Rewrite in progress. You ever heard me say "If you can't make it work without scripting first you likely have no business adding scripting to it"? Well, Yeah, that too.

    Gimme a few minutes to dot the t's and cross the i's.... and I'll post up a working demo to show scripting off and how scripting can enhance without replacing functionality.
     
    deathshadow, Nov 12, 2015 IP
  12. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,998
    Best Answers:
    253
    Trophy Points:
    515
    #12
    Alright, here we go, all better. Laughably documenting this is probably gonna take longer than writing it did. I'll toss a copy of this in the directory as a readme.txt just for later reference.

    I uploaded a copy to my server here of the 'working' code.
    http://www.cutcodedown.com/for_others/kewl/ajax/

    The demo is right there in a .rar, and I added some .source files so you can follow the bouncing ball.

    If you look at the documented copy of the script:
    http://www.cutcodedown.com/for_others/kewl/ajax/ajaxForms.js

    Comes in at 5.07k, I also tested a simple minify of it (which is what the demo is calling) and it shrinks down to 2.1k. By the time it's gzipped? 630 bytes... :D

    The whole thing is in a self-initiating anonymous function, since it's single run the usual bitching about anonymous functions does not apply, and it isolates the scope so the odds of cross-script interference is minimized. I pass document and window to it as d and w as local scope evaluates faster than global, and it shrinks the code a hair.

    I start out with some helper functions as before, I added all the typical ones I use a lot, and made a couple new ones to help with form processing. Laughably some of these functions exist in modern browsers like the class manipulation, but my routines are actually faster in FF and IE9/newer than the actual browser ones! (sorry, brute force scripting with regex should NOT be faster than native routines!)

    The formValuesAsURI function got a wee bit more tweaking done to it. Just pass it the form you want to serialize and boom, it handles it. I have it intentionally skip elements without names, and added special case handling for <select> since you either need the value or the .text of the selected <option>. Modern browsers all return the innerText as .value if you don't state a value, while legacy IE (7/earlier) do not, using .text instead. Simple or check for that.

    Into our acutal custom per this routines, first up is processUserInputs. This goes through all inputs in a form calling the passed callback handler() function if the input is not a submit, reset or hidden. You can also pass the optional skipSelect parameter as true to have selects skipped over, which we'll need for setting the values to empty after a successful submit.

    The submitHandler is called, well... onsubmit. The first thing it does is try to figure out if we have an event or need window.event, cancelling bubbling/propagation in the process using my handy eventProcess() function.

    The readyStateHandler function is inside submitHandler so that any variables local in scope to the submitHandler will exist when it is called. I completely spaced that legacy IE does not pass an event or record window.event, so you HAVE to use this method of passing the parent for legacy IE support.

    A lot of it's internals won't quite make sense yet, as we're calling things defined later, but let's go over it anyways.

    As you can see it just waits to get a state 4, and when recieved first removes the "sending" class from the form if present, and re-enables all input/select/submit/button tags in the form. If it's a 200, we sent the response text to our respond element, empty out all elements EXCEPT the select (a more robust version should return the selectedIndex to either the first <option> that's "selected" or 0 -- I may implement this later), and then exits. If it's NOT a 200, we don't flush the values and return the response code in the message area.

    I used a switch there as it's easier if you decide to add separate handlers for different possible response states like 300 or 500 series.

    Moving on inside submitHandler I pull the form from the event. A submit event's handler always returns the form as it's target in modern browsers, or srcElement in legacy IE. I then pull the ID of the response box from the form's data attribute. I use getAttribute for this as legacy IE doesn't really have proper data attribute support, and this method works in all browsers that support JS. I always try to test for if things exist and return false so as to have graceful handling of errors instead of bombing out and having to dive into the console.

    Finally, we make our AJAX request, I use the form's action attribute as the target (you'll see why when we get to the PHP)... and that's our local vars for the submitHandler.

    From there we just on submit set the class "sending" on the form (hence why it's unset on state 4). I like to do this as a hook for styling -- for now I just change the background, a better approach would probably be to use generated content on modern browsers to make a little spinny thing in CSS3, or load a animated gif as an overlay on older browsers. Change the response area text to say "sending" as a further indication that yes, we're doing something, and then disable all the inputs so the user can't try to use the form while we're processing it.

    Set the request header, send, and that's our submit handler. Notice I add '&fromAjax=1' to it, we'll get to that shortly.

    The final bit of code just pulls all forms on the page, iterates through them to see if they have the .ajaxMe class, and if so attaches our submitHandler to them.

    ... end result of all that? Any form with the .ajaxMe class when scripting is enabled will send via ajax instead of normal request -- but because the form is created normally it can also function properly scripting off as a full pageload.

    Why go to the trouble, well take a gander into test.php, the file users would actually call:
    http://www.cutcodedown.com/for_others/kewl/ajax/test.php.source

    I started it with another handy function -- I get sick of checking isset and/or array_key_exists every blasted time I want to do an if statement. :p

    But the real functionality is that it tests if 'fromForm' is the value we want. This can be handy to process multiple forms from one handling central PHP file. If $_POST['fromForm'] is 'test' we know we came from the form to process the result, so we set the message to say "sent successfully", and I added some debug output to it. if $_POST['fromAjax'] is set, we just wnat to return the message as ajax handled it properly -- easiest way to do that is to simply output the response using die. Execution stops right there.

    If $_POST['fromForm'] wasn't set, we simply set the message to the default a new page would get. Either way (not fromForm or not fromAjax) we include the form to show it.

    End result, little if any different in functionality scripting on or off apart from with scripting saving a few bytes on the pageload. If you look at the source of the template:

    http://www.cutcodedown.com/for_others/kewl/ajax/testForm.template.php.source

    You can see it's just a normal well-formed form, that has a <pre> tag with the id "response1" that echoes out the contents of $message inside it. Everything else is handled by the script loading right before </body>.

    Loading it there means the DOM is already built, so we don't need to dick with onload, and usually results in a faster page-load as well. I gave it at least one each of the major element types just to be sure the serialize routine was working.

    Tested in latest releases of FF, Chrome, ChrOpera and Vivaldi, as well as IE Edge, IE 10, and the emulation modes for IE 5 through IE 9 and it seems to check out. I'm currently not set up for ACTUAL legacy IE testing at the moment as I just did a fresh Win 8.1 install on this workstation since Win 10 can go plow itself. (NOT a fan)

    Seems to check out, took me a bit to get back in the groove as I've not dug into JS this deep in a few months.

    So... hope that helps. I figured just handing you code without a full explanation wouldn't be as helpful as actually documenting it. As you can see it's a wee bit more complex than you might thing, particularly if you want to support multiple forms on a page at once and older browser versions -- it's part of why people dive for jQuery so often, but really it's NOT an insurmountable mountain or so much code that jQ actually ends up any easier; particularly given how cryptic it can be.

    I may keep expanding the functionality of this a bit, like adding the selectedIndex reset -- right now it's also not disabling submit or reset, and that should be trapped as well.
     
    deathshadow, Nov 12, 2015 IP
  13. KewL

    KewL Well-Known Member

    Messages:
    245
    Likes Received:
    16
    Best Answers:
    3
    Trophy Points:
    128
    #13
    Sweet, works great man. Thanks for writing this all out. I need to learn some of the javascript short had your using, should probably learn some regex too. I can't understand quite a bit of the code you wrote.
     
    KewL, Nov 12, 2015 IP
  14. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,998
    Best Answers:
    253
    Trophy Points:
    515
    #14
    I just tossed up a more robust version here:
    http://www.cutcodedown.com/for_others/kewl/ajax2/

    That fixes some quirks/bugs, disables all the user inputs properly while preserving any that are intentionally disabled, and so forth.

    I'm half tempted to mix it with my client side validation script, but I don't waste time validating client side or using AJAX since if the page and the form are so complex and your caching techniques are so poor that re-sending the markup has any significant penalty over AJAX or client-side handling, there's something wrong with your html or form.

    Admittedly, with most people's sites there's something WRONG with their HTML and forms... like CtC's of 10:1, with endless static CSS and static scripting in their markup in that "separation of presentation from content, what's that?!?" ignorance.

    Or worse, the scripttards and framework using dipshits who actually attack the good practice of separation.
     
    deathshadow, Nov 13, 2015 IP
  15. KewL

    KewL Well-Known Member

    Messages:
    245
    Likes Received:
    16
    Best Answers:
    3
    Trophy Points:
    128
    #15
    Nice, checking out the robust version as we speak. I'd be curious to see your form validation techniques, I really have no need for client-side validation on this specific site (its so simple). I just have the server check the email address and that the other two fields exist, responding accordingly.

    I need to learn this short hand stuff.
    function eventAdd(e, event, handler) {
      if (e.addEventListener) e.addEventListener(event, handler, false);
        else e.attachEvent('on' + event, handler);
    }
    Code (javascript):
    2 line if else with no {}{}{}?

    How about this one:
    (typeof newNode == 'object') ? newNode : d.createTextNode(newNode)
    Code (javascript):
    No idea whats happening above :S
     
    KewL, Nov 13, 2015 IP
  16. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,998
    Best Answers:
    253
    Trophy Points:
    515
    #16
    If you only have one statement you don't actually "need" curly brackets. All three of these:
    if (--a) { b++; } else { b += 10; }
    
    if (--a) b++; else b += 10;
    
    if (--a) b++;
      else b += 10;
    Code (markup):
    Are functionally identical; remember that JavaScript is a whitespace neutral language. When things start to hit up against being 76 characters wide I like to put them on a newline -- to me it's just easier to follow that way than the dumbass "let's stuff everything onto a line that wraps seven times so you can't tell where anything starts or ends" nonsense. See what a lot of people do in their CSS

    ALL that curly brackets do is tell the interpreter "treat these all as if they were one statement". If you only have ONE statement, the only time they serve a real purpose is to enhance code clarity and/or waste a few bytes... which in an INTERPRETED language is something to watch out for.

    That second snippet is just an inline evaluation, which some people also call ternary operations. They're one of the more powerful and simple bits of all C syntax languages. before the ? is the condition, after the ? is what's run as true, what's after the : is run as false.

    Which is why this:

    b += --a ? 1 : 10;
    Code (markup):
    Would be functionally identical to the above if statements. We subtract one from A, if it's non-zero we add one, otherwise we add ten.

    That newNode part is handy in that if you pass newNode a object, it's appended as is, so you could pass it a generated element like document.createElement('a) and it will put that anchor on it. If it's not an object, we add it as a string with document.createTextNode.

    If you look at the entire function:
    function nodeAdd(e, newNode) {
    	e.appendChild(
    		(typeof newNode == 'object') ? newNode : d.createTextNode(newNode)
    	);
    }
    Code (markup):
    It just quickly does an appendChild based on what you pass it. If you pass it the handle to an anchor, it would be the same as calling:

    e.appendChild(yourObject);

    if you pass it a string or a number, it's the same as

    e.appendChild(document.createTextNode('Some Text'));

    I just like having one central smart function that figures out the right method or appending nodes. Makes more sense in the complete library where I also have a 'nodeInsert' which instead of appendChild it does a:

    e.insertBefore(
    	(typeof newNode == 'object') ? newNode : d.createTextNode(newNode),
    	e.firstChild
    );
    Code (markup):
    VERY handy if you want to make a new element, like say an anchor that only functions when scripting is working and as such has ZERO blasted business in the markup.

    var newA = document.createElement('a');
    newA.href="/whatever";
    neaA.className="whatever";
    nodeAdd(newA, 'Go to Whatever');
    nodeAdd(document.getElementById('contactForm'), newA);
    Code (markup):
    Which while functionally identical to:

    document.getElementById('contactForm').innerHTML +=
    	'<a href="/whatever" class="whatever">Go To Whatever</a>';
    Code (markup):
    Executes many, MANY times faster than the latter as innerHTML triggers a full reparse of the entire document (which can also trigger unexpected scripting behaviors) instead of just going straight to the DOM.

    It's a little more work to code, it's a little more code, but it's faster, cleaner and IMHO easier to follow.

    More so if you create a little function to do the gruntwork for you. I have a "library" of helper functions I pull things from, that I only include what I'm using. I dislike calling it a framework thanks to the negative connotation things like YUI, mootools and jQuery have given them, much less the implication of changing how javaScript works. That's one of my biggest problems with jQuery and it's ilk is they flat out don't work how the underlying language does, or how I think about programming solutions.

    function splitSelector(s) {
    	var
    		r = { tag : 'div', id : false, classes : false },
    		t = s.indexOf('.');
    	if (t >= 0) {
    		r.classes = s.substr(t + 1).replace('.', ' ');
    		s = (t > 0) ? s.substr(0, t) : '';
    	}
    	if ((t = s.indexOf('#')) >= 0) {
    		r.id = s.substr(t + 1);
    		s = (t > 0) ? s.substr(0, t) : '';
    	}
    	if (s.length > 0) r.tag = s;
    	return r;
    }
    
    function make(selector, parent, content, attributes) {
    	var
    		s = splitSelector(selector),
    		e = d.createElement(s.tag);
    	if (s.id) e.id = s.id;
    	if (s.classes) e.className = s.classes;
    	if (parent) parent.appendChild(e);
    	if (content) e.appendChild(
    		typeof content == "object" ? content : d.createTextNode(content)
    	);
    	if (attributes)
    		for (attr in attributes) e.setAttribute(attr, attributes[attr]);
    	return e;
    }
    Code (markup):
    Might seem a bit complex, but it reduces making a new element as a child of another to:

    make(
    	'a.whatever',
    	document.getElementById('contactForm'),
    	'Go to Whatever',
    	{ href : '/whatever' }
    );
    Code (markup):
    Giving you most all the speed of the flat JS version, and IMHO being simpler than what jQ does by a stretch. The above code is semi-big, but pays for itself if you're adding a half dozen or more elements like the controls for a scripted slideshow -- the type of thing that again has no business in the markup in the first place.

    Hence why 90% of the time if I'm making a slideshow (yeah, right) the markup is little more than:

    <div class="slideShow">
    	<div>
    		<img src="images/slide1.png" alt="some slide">
    		<span>Describe the first slide</span>
    	</div>
    	<div>
    		<img src="images/slide2.png" alt="some slide">
    		<span>Describe the second slide</span>
    	</div>
    	<div>
    		<img src="images/slide2.png" alt="some slide">
    		<span>Describe the third slide</span>
    	</div>
    <!-- .slideShow --></div>
    Code (markup):
    With EVERYTHING else added by the scripting or handled by the CSS... Yes, even when having play/pause, progress to next slide indicators, current slide indicators, etc, etc. Laughably, most of the time all the script needs to do for changing the slide is do a class swap since again, let CSS do the heavy lifting instead of dicking around with Element.style in the script.

    You also asked about my validation method -- typically I have that tied to the PHP side of things as I generate my form off an array containing the type of input, label, and validation method. I pass the validation methods to my inputs as v_type. So a input might look like:

    <input type="text" name="name" id="contactName" class="v_required">

    I then just go through all the elements, and check for the existence of those classes onsubmit to run checks on inputs I want checked. When there's an error I throw a v_error class on the input so I can colour that a mistake was made, and append a paragraph <p class="error">This field is required</a> after the input to say what went wrong. I do this to all elements at once instead of short circuiting out so that ALL errors are marked, not just the first one. (I've seen WAY too many validation scripts only stop at the first error -- like, what the?)

    Nowadays I'd consider using data- attributes for that, but really using classes makes it easier to target them for style in addition to from the scripting. NOT that I'd actually bother with scripted validation thanks to HTML 5 element types (one of the few things from 5 I like) and that if your form is "big enough" or "complicated enough" or just flat out sucks so bad that one pageload is the end of the world, it's time to toss the whole mess and start over clean.

    Of course, since I'm using classes and paragraphs for this, I have the server-side validation operate the exact same way when an error is found; partly because you can't EVER trust client-side validation, partly because I write the page first to work WITHOUT scripting and then only use JS to enhance it. Scripting on or off, my forms work nearly identical, which is why I don't bother wasting time or bandwidth on JS for most forms; I'd only use it if processing a lot of user input or for convenience like quick reply on a forum skin.

    As you'll hear me say time and time again, the unwritten rule of JavaScript: "If you can't make the page work first without scripting, you likely have no business adding scripting to it." -- to that end good scripting should enhance functionality, not supplant it or be the means of providing it.

    At least if you care more about making a page that's useful to users than you do artsy fartsy BS.
     
    Last edited: Nov 13, 2015
    deathshadow, Nov 13, 2015 IP