Div inline with form field in parent div

Discussion in 'CSS' started by joshdmitchell, Dec 26, 2007.

  1. #1
    Hi there,

    I'm really struggling to get two div's to appear inline within a parent div (code below). There are javascript elements that require this structure (for text to appear when field selected). Just simply need to get the div "usernote" to appear inline with the form field, as opposed to under the field.

    Any help would be greatly appreciated!

    Thanks!
    Josh

    
    
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
          "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml" >
    <head>
      <meta http-equiv="content-language" content="en" />
      <title>User Registration</title>
    <style>
    html, body {
      color: #000;
      background-color: #fff;
      font-size:15px;
    }
    
    form div.active {
      background-color: #F5F5DC;
      border: 0px solid #8a8;
    }
    
    #username
    {
    	font-family:Arial;
    	font-weight:bold;
    	padding-top: 1em;
    	padding-bottom: 1em;
    	width:300px;
    	padding-left:20px;
    }
    
    #usernote
    {
    	font-weight: 400;
    	font-family:Arial;
    	padding: 0em;
    	font-size:12px;
    	width:300px;
    }
    
    #user_registration
    {
    	border:1px solid #6495ED;
    	width:700px;
    	background-color: #ECF1EF;
    	margin-left:100px;
    }
    
    #user_registration p
    {
    	clear:both;
    	margin-top:0px;
    	margin-bottom:0px;
    }
    
    </style>
    <script type="text/javascript"><!--
    function hasClassName(el,c){
      c=c.replace(/\-/g,'\\-');
      return (new RegExp('(^|\\s)'+c+'(\\s|$)')).test(el.className);
    }
    function addClassName(el,c){
      if(hasClassName(el,c)) return;
      var curClass=el.className||'';
      el.className=curClass+((curClass.length>0)?' ':'')+c;
    }
    function removeClassName(el,c){
      var re=new RegExp('\\s*'+c.replace(/\-/g,'\\-')+'\\s*');
      el.className=el.className.replace(re,' ').replace(/^\s*|\s*$/g,'');
    }
    function highlightElm(el,light){
      if(!el) return;
      if(light) addClassName(el,'active');
      else removeClassName(el,'active');
    }
    window.onload = function(){
    	document.getElementById('field1').onblur = function() {
    	document.getElementById('usernote').style.display = 'none';
    	highlightElm(this.parentNode, false);
    }
    	document.getElementById('field1').onfocus = function() {
        document.getElementById('usernote').style.display = 'block';
        highlightElm(this.parentNode, true);
    }
    }
    
    // -->
    </script>
    
    
    </head>
    <body>
    
    <form method="post" action="" id="user_registration" name="user_registration">
    <div id="username">
    	Username
    	<p></p>
    	<input type="text" id="field1" name="field1" size="30" tabindex="1">
    
    	<div id="usernote" style="display:none;">
    	Your username can only contain letters (A-Z) or numbers (0-9)
    	</div>
    </div>
    
    
    </form>	
    
    </body>
    </html>
    
    Code (markup):
     
    joshdmitchell, Dec 26, 2007 IP
  2. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,999
    Best Answers:
    253
    Trophy Points:
    515
    #2
    Well, let's see... This will be a top-down list of what I see 'wrong'

    XHTML 1.1. with no XML prolog - so it's invalid XHTML before we get past the second line - NOT that a website that is going to actually be deployed should EVER, EVER use XHTML 1.1 since browser support for valid 1.1 is about as widespread as Safari and Opera use combined. Drop to a 1.0 strict doctype so you can validate AND not throw IE into quirks mode since 1.0 the prolog is optional (1.1 it is NOT)... and include a lang definition for browsers/engines that don't recognize xml:lang.

    No character encoding declared.

    No universal reset or margin/padding resets, kiss off anything you write working cross-browser.

    You don't need the attribute 'name' on inputs in XHTML if you have an identical ID

    Ah, here's your actual issue: Inputs are inherently inline - to make the other element appear on the same line, just use a span instead of a div.

    MAJOR case of classitus.

    The empty paragraph makes no sense, and the text above that should probably be a LABEL.

    with no padding, the background-color on the form will not show, making the border on the inner DIV kind of pointless.

    100px side margin with a 700px wide form != 800 friendly, need to chop 32px off that.

    The focusing javascript is also more convoluted than need be - if you can change the parent containers class, you don't NEED to change the display property on the fieldtext, you can inherit that property in the CSS.

    Additionally, creating the function directly on each 'field' means if you have a bunch of them on the page, you are NOT sharing the same codebase making the .js consume more memory than need be - so split those inline functions out to their own standalones

    On that same note, hasClassName is more complex than need be:
    function hasClassName(el,c) {
    	return (el.className.search(new RegExp('/\b'+c+'\b/g'))>=0);
    }
    Code (markup):
    Just as good... and ends up so short I would NOT waste the overhead on making it a separate function since it is just one line of code.

    Likewise addClassName and removeClassName are equally 'overthought' - addclassname uses short circuit exit instead of flow, (which is actually SLOWER in most languages), removeClassName runs multiple replace conditions when you only need one, etc, etc... so I'd simplify both of those down to

    function addClassName(el,c){
    	thisClass=el.className;
    	if (thisClass.search(/\bactive\b/g)==-1) {
    		thisClass+=' '+c;
    		el.className=thisClass.replace(/^\s+|\s+$/g,'');
    	}
    }
    
    function removeClassName(el,c){
    	el.className=el.className.replace(new RegExp('/\b'+c+'\b/g'),'');
    }
    
    Code (markup):
    Which again, that second one is so short I'd not make it a separate function, and likewise I don't think I'd make addClassName a separate function either, it's excess overhead for no real gains....

    Here is how I'd approach this problem:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
    
    <meta
    	http-equiv="Content-Type"
    	content="text/html; charset=iso-8859-1"
    />
    
    <title>User Registration</title>
    
    <style type="text/css">
    * {
    	margin:0;
    	padding:0;
    }
    
    html, body {
    	font:normal 85%/140% arial,helvetica,sans-serif;
    	color:#000;
    	background:#FFF;
    }
    
    #userRegistration {
    	width:668px;
    	margin:1em 0 0 100px;
    	background:#ECF1EF;
    	border:1px solid #6495ED;
    }
    
    #userRegistration div {
    	border:1px solid #6495ED;
    	padding:8px;
    	margin:8px;
    }
    
    #userRegistration .active {
    	background:#F5F5DC;
    	border-color:#8A8;
    }
    
    #userRegistration label {
    	display:block;
    	padding:8px 0 16px 20px;
    	font:bold 100%/140% arial,helvetica,sans-serif;
    }
    
    #userRegistration span {
    	display:none;
    	width:300px;
    	font:bold 12px/14px arial,helvetica,sans-serif;
    }
    
    #userRegistration .active span {
    	display:inline;
    }
    
    </style>
    
    <script type="text/javascript"><!--
    
    function targetFieldOnBlur() {
    	this.parentNode.className=this.parentNode.className.replace(/\bactive\b/g,'');
    }
    
    function targetFieldOnFocus() {
    	thisClass=this.parentNode.className;
    	if (thisClass.search(/\bactive\b/g)==-1) {
    		thisClass+=' active';
    		this.parentNode.className=thisClass.replace(/^\s+|\s+$/g,'');
    	}
    }
    
    function initialize() {
    	targetForm=document.getElementById('userRegistration');
    	fieldList=targetForm.getElementsByTagName('input');
    	for (t=0; t<fieldList.length; t++) {
    		targetField=fieldList[t];
    		if (fieldList[t].className.search(/\bfocusing\b/g)>=0) {
    			fieldList[t].onblur=targetFieldOnBlur;
    			fieldList[t].onfocus=targetFieldOnFocus;
    		}
    	}
    }
    
    if (window.addEventListener) {
    	window.addEventListener("load",initialize, false);
    } else if (window.attachEvent) {
    	window.attachEvent("onload",initialize);
    } else if (document.getElementById) {
    	window.onload=initialize;
    }
    
    --></script>
    
    </head><body>
    
    <form method="post" action="" id="userRegistration">
    	<div>
    		<label for="username">Username</label>
    		<input type="text" id="username" size="30" class="focusing" />
    		<span>
    			Usernames can only contain letters (A-Z) or numbers (0-9)
    		</span>
    	</div>
    	<div>
    		<label for="password">Password</label>
    		<input type="password" id="password" size="30" class="focusing" />
    		<span>
    			Passwords can only contain letters (A-Z) or numbers (0-9)
    		</span>
    	</div>
    </form>	
    
    </body></html>
    Code (markup):
    The formatting is a little different because I was seeing four different appearances in four different browser engines, so I took a wild guess on the appearance. I also tacked on a autodetection routine to allow you to run multiple 'sections' (as illustrated by the password box) - AND because I target the parentNode only it means you can have multiple inputs inside the same div target the containing div simultaneously - all set up automatically by targeting the .focusing class - I'd target all inputs, but there may be cases where you don't want all inputs nabbed (like submits or radio buttons), so that extra class handles that. I also gave you the full-on failsafe onload method.

    Validates XHTML 1.0 Strict, tested working IE 5.5, 6 & 7, FF, Opera and Safari.

    Hope this helps.
     
    deathshadow, Dec 27, 2007 IP
  3. Stomme poes

    Stomme poes Peon

    Messages:
    3,195
    Likes Received:
    136
    Best Answers:
    0
    Trophy Points:
    0
    #3
    Good catch on the for's, I was just going to add that : )
     
    Stomme poes, Dec 27, 2007 IP
  4. Mike H.

    Mike H. Peon

    Messages:
    219
    Likes Received:
    11
    Best Answers:
    0
    Trophy Points:
    0
    #4
    Another approach:
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
       "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <title>Any Title</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <script type="text/javascript">
    
    	function toggleMessage(nInput){
    
    		var currField = nInput.parentNode.nextSibling.nextSibling;
    		currField.style.display == "none" ? currField.style.display = "" : currField.style.display = "none";
    	}
    
    	function init(){
    
    		var nForm = document.forms[0];
    		var nSpan = nForm.getElementsByTagName('span');
    		for (i=0; i<nSpan.length; i++)
    			{
    			 nSpan[i].style.display = "none";			 
    			}
    		var nLabel = nForm.getElementsByTagName('label');		
    		for (i=0; i<nLabel.length; i++)
    			{
    			 nLabel[i].childNodes[1].onfocus = function(){toggleMessage(this)}				
    			 nLabel[i].childNodes[1].onblur = function(){toggleMessage(this)}				
    			}
    	}
    
    	onload=init;
    	
    </script>
    <style type="text/css">
    
    	 body {background-color: #fffacd; margin-top: 60px;}
    	 form {width: 600px; margin: auto; font-family: arial; font-size: 12pt;}	
    	 fieldset {background-color: #eee8aa; border: 1px solid #e6b280; padding-left: 8px; padding-bottom: 8px;} 
    	 legend {font-family: arial; font-size: 14pt; color: #000000; background-color: transparent; padding-top: 3px; padding-left: 3px; padding-right: 3px; margin-bottom: 5px;}
    	 label {font-family: arial; font-size: 12pt;}
    	 form span {font-family: arial; font-size: 10pt; padding-left: 10px;}
    	.submitBtn {background-color: #ffffff; border: solid 1px #000000; font-family: arial; font-size: 10pt; font-weight: bold; cursor: pointer; display: block; margin-left: auto; margin-right: auto; margin-top: 10px; margin-bottom: 5px;}
    	
    </style>
    </head>
    	<body>
    		<form method="post" action="">
    		   <fieldset>
    			<legend>Registration Form</legend>
    
    				<label>Username:&nbsp;<input type="text" name="username" size="30"></label>
    				<span>Use letters (A-Z) or numbers (0-9) only</span>
    				<br><br>
    				<label>Password:&nbsp;<input type="password" name="password" size="30"></label>
    				<span>Use letters (A-Z) or numbers (0-9) only</span>
    
    				<input type="submit" name="submit" value="Submit" class='submitBtn'>
    		   </fieldset>
    		</form>		
    	</body>
    </html>
    
    Code (markup):
     
    Mike H., Dec 27, 2007 IP
  5. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,999
    Best Answers:
    253
    Trophy Points:
    515
    #5
    Interesting - though I'd be worried about the fact that fieldsets and legends completely SUCK ASS cross browser, (which is why I pretty much gave up on using them)... and I'm not sure I care for the idea of using nextsibling given it pretty much DEMANDS that you keep the elements in the same order - which by placement makes a degree of sense, but swapping the parent would be easier than directly manipulating a sibling.

    condensing the 'toggle' to a single method makes a degree of sense, but by manipulating display and using it to store the state if you double click too rapidly FF will invert the states (since it will drop methods that are the same on loops - which is #DDD but what can you do)... and you don't do anything about styling the parent container, which is definately part of what he wanted. (took me a bit on my version to realize that's what the .active class was for)

    Of course, I also don't trust accessing document.forms as far as I could throw the USS Iowa so there's that issue too.

    Also, you've got some REALLY wierd code there... like this:
    nLabel.childNodes[1].onfocus = function(){toggleMessage(this)}

    What the??? You've already GOT a function, 'this' is defined by all functions inherently - there's no reason for all that. Remember, when you attach a method to an object, 'this' inside that method will always refer to said object.

    nLabel.childNodes[1].onfocus=toggleMessage;

    functionally identical... though toggleMessage would have to be rewritten to access 'this' instead of 'ninput'. Also, that togglemessage functions logic flow is just whacko.

    function toggleMessage(){
    	currField = this.parentNode.nextSibling.nextSibling;
    	currField.style.display=(currField.style.display == "none" ? "" : "none");
    }
    Code (markup):
    makes a hell of a lot more sense (and a good deal less code)

    I do agree with it on hiding the focused text in the javascript instead of having it start hidden in the CSS -that way when javascript is off/missing/etc, the text will show on all fields. Graceful degredation and such.

    Of course, the AT&T style formatting is enough to make one want to take a hot poker to one's eyeballs (I say, I say, that's a joke son.)... not to mention the pt sized fonts inside a narrow little container making the whole form jump on large font machines... ;) Much illegible mess that condensing the CSS to single lines creates, especially with no property condensing.
     
    deathshadow, Dec 27, 2007 IP