SitePoint Sponsor

User Tag List

Results 1 to 5 of 5
  1. #1
    Barefoot on the Moon! silver trophy
    Force Flow's Avatar
    Join Date
    Jul 2003
    Location
    Northeastern USA
    Posts
    4,516
    Mentioned
    51 Post(s)
    Tagged
    1 Thread(s)

    moving elements up and down

    How would you go about moving elements up and down on the screen, given something like this:

    Code:
    <form id="form1">
    <fieldset id="set1">
    <button type="button" onclick="moveup('set1')">/\</button><button type="button" onclick="movedown('set1')">\/</button>
    <div>stuff here</div>
    </fieldset>
    <fieldset id="set2">
    <button type="button" onclick="moveup('set2')">/\</button><button type="button" onclick="movedown('set2')">\/</button>
    <div>stuff here</div>
    <fieldset id="set3">
     <button type="button" onclick="moveup('set3')">/\</button><button type="button" onclick="movedown('set3')">\/</button>
     <div>stuff here</div>
    </fieldset>
    </form>

    I was doing some googling and poking through some of prototype's documentation, but I couldn't come up with anything.

    Since the elements are listed in that particular way on a page, is it even possible to reposition them?

  2. #2
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,526
    Mentioned
    83 Post(s)
    Tagged
    3 Thread(s)
    Yes, that can be done with little trouble. I wouldn't use onclick attributes though, and the buttons shouldn't be in the html code itself, for what happens when javascript is not supported? Those buttons then become meaningless and confusing.

    So let's get rid of them from the html and create them from javascript instead.

    Code html4strict:
    <form id="form1">
    	<fieldset id="set1">
    		<div>set1 stuff here</div>
    	</fieldset>
    	<fieldset id="set2">
    		<div>set2 stuff here</div>
    	</fieldset>
    	<fieldset id="set3">
    		 <div>set3 stuff here</div>
    	</fieldset>
    </form>

    Code javascript:
    var fieldsets = document.getElementsByTagName('fieldset');
    var el;
    for (var i = 0; i < fieldsets.length; i++) {
    	el = createElement('button', '\\/', {type: 'button'}, {onclick: movedown});
    	fieldsets[i].insertBefore(el, fieldsets[i].firstChild);
    	el = createElement('button', '/\\', {type: 'button'}, {onclick: moveup});
    	fieldsets[i].insertBefore(el, fieldsets[i].firstChild);
    }

    What is that createElement() function? That's something I whipped up to make creating elements a whole lot easier. Creating elements often takes quite a few statements, so with the createElement() function it becomes just a one-liner.

    Code javascript:
    // before
    el = document.createElement('button');
    text = document.createTextNode('/\\');
    el.type = 'button';
    el.onclick = moveup;
    el.appendChild(text);
    // after
    el = createElement('button', '/\\', {type: 'button'}, {onclick: moveup});

    The createElement() function that makes the job of creating elements a whole lot easier, so instead of duplicating a lot of lines to create elements and their parts, this is the function that's used instead.

    Code javascript:
    function createElement(tag) {
    	// create element
    	var el = document.createElement(tag);
    	var arg;
    	for (var i = 1; i < arguments.length; i++) {
    		arg = arguments[i];
    		if (typeof arg === 'string') {
    			// append text
    			el.appendChild(document.createTextNode(arg));
    		} else if (typeof arg === 'object') {
    			for (var prop in arg) {
    				if (prop.substring(0, 2) !== 'on') {
    					// attach attribute
    					el.setAttribute(prop, arg[prop]);
    				} else {
    					// attach event
    					el[prop] = arg[prop];
    				}
    			}
    		}
    	}
    	return el;
    }

    So now we're back to where you were before, but instead the arrow buttons will appear only when javascript is able to be run.

    Here are the functions that get run when you click on the arrow buttons.

    Code javascript:
    function moveup() {
    	var el = uptoElement(this, 'fieldset');
    	var parent = el.parentNode;
    	var previous = previousTag(el);
    	if (!previous) {
    		return;
    	};
    	parent.insertBefore(el, previous);
    }
    function movedown() {
    	var el = uptoElement(this, 'fieldset');
    	var parent = el.parentNode;
    	var next = nextTag(el);
    	var last = lastTag(el);
    	if (!next) {
    		return;
    	}
    	if (next !== last) {
    		parent.insertBefore(el, nextTag(next));
    	} else {
    		parent.appendChild(el);
    	}
    }

    When moving elements, we are going to be checking for the previous and next element, and as I'm not aware of an easy way to go through just the tag elements, we're going to have to deal with the text elements that contain the whitespace around the tags as well. So we'll use some helper functions, uptoElement(), previousTag(), nextTag(), and lastTag()

    Code javascript:
    function uptoElement(el, tag) {
    	while (el.nodeName !== tag.toUpperCase()) {
    		el = el.parentNode;
    		if (el.nodeName === 'BODY') {
    			return;
    		}
    	}
    	return el;
    }
    // ELEMENTNODE = 1, TEXTNODE = 3;
    function previousTag(el) {
    	el = el.previousSibling;
    	while (el && el.nodeType !== 1) {
    		el = el.previousSibling;
    	}
    	return el;
    }
    function nextTag(el) {
    	el = el.nextSibling;
    	while (el && el.nodeType !== 1) {
    		el = el.nextSibling;
    	}
    	return el;
    }
    function lastTag(el) {
    	el = el.parentNode.lastChild;
    	while (el && el.nodeType !== 1) {
    		el = el.previousSibling;
    	}
    	return el;
    }

    Here's the sample code

    Code html4strict:
    <html>
    <head>
    </head>
    <body>
    <form id="form1">
    	<fieldset id="set1">
    		<div>set1 stuff here</div>
    	</fieldset>
    	<fieldset id="set2">
    		<div>set2 stuff here</div>
    	</fieldset>
    	<fieldset id="set3">
    		 <div>set3 stuff here</div>
    	</fieldset>
    </form>
     
    <script>
    var fieldsets = document.getElementsByTagName('fieldset');
    var el;
    for (var i = 0; i < fieldsets.length; i++) {
    	el = createElement('button', '\\/', {type: 'button'}, {onclick: movedown});
    	fieldsets[i].insertBefore(el, fieldsets[i].firstChild);
    	el = createElement('button', '/\\', {type: 'button'}, {onclick: moveup});
    	fieldsets[i].insertBefore(el, fieldsets[i].firstChild);
    }
    function createElement(tag) {
    	// create element
    	var el = document.createElement(tag);
    	var arg;
    	for (var i = 1; i < arguments.length; i++) {
    		arg = arguments[i];
    		if (typeof arg === 'string') {
    			// append text
    			el.appendChild(document.createTextNode(arg));
    		} else if (typeof arg === 'object') {
    			for (var prop in arg) {
    				if (prop.substring(0, 2) !== 'on') {
    					// attach attribute
    					el.setAttribute(prop, arg[prop]);
    				} else {
    					// attach event
    					el[prop] = arg[prop];
    				}
    			}
    		}
    	}
    	return el;
    }
    function uptoElement(el, tag) {
    	while (el.nodeName !== tag.toUpperCase()) {
    		el = el.parentNode;
    		if (el.nodeName === 'BODY') {
    			return;
    		}
    	}
    	return el;
    }
    // ELEMENTNODE = 1, TEXTNODE = 3;
    function previousTag(el) {
    	el = el.previousSibling;
    	while (el && el.nodeType !== 1) {
    		el = el.previousSibling;
    	}
    	return el;
    }
    function nextTag(el) {
    	el = el.nextSibling;
    	while (el && el.nodeType !== 1) {
    		el = el.nextSibling;
    	}
    	return el;
    }
    function lastTag(el) {
    	el = el.parentNode.lastChild;
    	while (el && el.nodeType !== 1) {
    		el = el.previousSibling;
    	}
    	return el;
    }
     
    function moveup() {
    	var el = uptoElement(this, 'fieldset');
    	var parent = el.parentNode;
    	var previous = previousTag(el);
    	if (!previous) {
    		return;
    	};
    	parent.insertBefore(el, previous);
    }
    function movedown() {
    	var el = uptoElement(this, 'fieldset');
    	var parent = el.parentNode;
    	var next = nextTag(el);
    	var last = lastTag(el);
    	if (!next) {
    		return;
    	}
    	if (next !== last) {
    		parent.insertBefore(el, nextTag(next));
    	} else {
    		parent.appendChild(el);
    	}
    }
    </script>
    </body>
    </html>
    Last edited by paul_wilkins; May 8, 2008 at 20:50.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  3. #3
    SitePoint Guru
    Join Date
    Apr 2006
    Posts
    802
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Using input type buttons lets you call the function from any clicks that bubble up to the form,
    by seeing if any of the clicks are from a field with '\/' or '/\' value.

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
    <html lang="en">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>DOM insertBefore</title>

    <script type="text/javascript">
    // any number of fieldsets

    Code:
    window.onload= function(){
    document.getElementById('form1').onclick= function(e){
    	e= window.event || e;
    	var who= e.target || e.srcElement;
    	if(who.nodeType== 3)who= who.parentNode;
    	if(who.value){
    		var what= who.value;
    		if(what== '\/\\' || what== '\\\/'){
    			var pa= who.parentNode;
    			while(pa && pa.tagName != 'FIELDSET'){
    				pa= pa.parentNode;
    			}
    			var F= pa.parentNode;
    			if(what== '\/\\'){
    				var sib= pa.previousSibling;
    				while(sib && sib.tagName!= 'FIELDSET'){
    					sib= sib.previousSibling;
    				}
    				if(sib) F.insertBefore(pa,sib);
    			}
    			else{
    				var sib= pa.nextSibling;
    				while(sib && sib.tagName!= 'FIELDSET'){
    					sib= sib.nextSibling;
    				}
    				if(sib) F.insertBefore(sib,pa);
    			}
    			if(sib) who.focus();
    		}
    	}
    }
    }
    </script>

    </head>

    <body>
    <h1 id="p_opts">DOM insertBefore test </h1>
    <form id= "form1">
    <fieldset>
    <input type= "button" value="/\">
    <input type= "button" value="\/">
    <div>1.stuff here</div>
    </fieldset>
    <fieldset >
    <input type= "button" value="/\">
    <input type= "button" value="\/">
    <div>2.stuff here</div>
    </fieldset>
    <fieldset >
    <input type= "button" value="/\">
    <input type= "button" value="\/">
    <div>3.stuff here</div>
    </fieldset>
    </form>

    </body>
    </html>

  4. #4
    Barefoot on the Moon! silver trophy
    Force Flow's Avatar
    Join Date
    Jul 2003
    Location
    Northeastern USA
    Posts
    4,516
    Mentioned
    51 Post(s)
    Tagged
    1 Thread(s)
    first, thanks for the examples guys. Since I know ahead of time the browsers accessing this page will have javascript turned on, generating the buttons via javascript just ended up being way too complex.


    Anyway, I took this a few steps further using mrhoo's example.

    Bascially, I now have a remove button in addition to the move up and down buttons.

    Also, I have a separate input id "mediaorder" to keep track of what's been moved and what's been removed.

    While this seems to work in firefox, this doesn't work at all in IE6. I'm not sure why, since mrmoo's original script worked in both IE and firefox.

    Code:
    <!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">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    <title></title>
    <link rel="stylesheet" href="CSS/defaults.css" type="text/css" />
    <link rel="stylesheet" href="CSS/css_forms3.css" type="text/css" />
    <!--[if IE]>
    <link rel="stylesheet" href="CSS/css_forms_iefix.css" type="text/css" />
    <![endif]-->
    <style type="text/css">
        div.preview_img input {
            margin-bottom: 110px;
        }
        span.button_updown{
            margin-right: 8px;
        }
        span.button_remove {
            margin-left: 8px;
        }
        
        button.button_updown, button.button_remove, button.button_updown_pos_hover, button.button_updown_neg_hover {
            cursor:pointer;
            border:outset 1px #ccc;
            color:#272727;
            font-weight:normal;
            background:url("images/forms/buttonbg.gif") repeat-x left bottom;
            padding: 0px 4px 0px 4px;
            font-size: 80%;
        }
        
        button.button_updown_pos_hover, button.button_updown_neg_hover {
            border-color:black;
        }
        
        button.button_updown_pos_hover {
            background:url("images/forms/buttonbg_green.gif") repeat-x left bottom;
        }
        button.button_updown_neg_hover {
            background:url("images/forms/buttonbg_red.gif") repeat-x left bottom;
        }
    
    </style>
    <script type="text/javascript">    
        
        Array.prototype.findindex = function(name){
            var index;
            for (var i = 0; i < this.length; i++){
                //alert("arrayitem["+i+"]: " + this[i]);
                if (this[i]==name){
                    //alert("matched arrayitem["+i+"]: " + this[i]);
                    index=i;
                    break;
                }
            }
            //alert("index:"+index);
            return index;
        }
        
        Array.prototype.removeItem = function(valToRemove){
            
            // Normalize to a string like !val!!val!!val!
            var s = '!' + this.join('!!') + '!';
            alert(s);
            // Remove targeted values with delimiters
             s = s.replace(new RegExp('!' + valToRemove + '!', 'g'), '');
            // Remove delimiter added to end in step 1
            s = s.replace(/^!/, '');
            // Remove delimiter added to start in step 1
            s = s.replace(/!$/, '');
            // Convert to array
            return s.split('!!');
        }
        
        
        function remove_hidden_selected(name){
            var mediaorder=document.getElementById('mediaorder').value.split(",");
            mediaorder=mediaorder.removeItem(name);
            document.getElementById('mediaorder').value=mediaorder.join(',').toString();
        }
        
        
        function move_hidden_selected(name, direction){
            var mediaorder=document.getElementById('mediaorder').value.split(",");
            
            var index = mediaorder.findindex(name);
            var index2=0;
            
            if(direction=="up"){
                index2=index+1;
            }
            else if(direction="down"){
                index2=index-1;
            }
            
            alert("tomove:"+name+" direction:"+direction);
            alert("selectedindex: "+ index + " switchwith:" + index2);
            alert("selected:" + mediaorder[index] + " switchwith:" + mediaorder[index2]);
            mediaorder[index]=mediaorder[index2];
            mediaorder[index2]=name;
            
            document.getElementById('mediaorder').value=mediaorder.join(',').toString();
            
            alert(mediaorder.join(',').toString() + "\n"+ document.getElementById('mediaorder').value);
        }
        
    
        
        window.onload= function(){
            document.getElementById('form1').onclick= function(e){
            e= window.event || e;
            var who= e.target || e.srcElement;
            if(who.nodeType== 3)who= who.parentNode;
            if(who.value){
                var what= who.value;
                if(what== 'moveup' || what== 'movedown'){
                    var pa= who.parentNode;
                    while(pa && pa.tagName != 'FIELDSET'){
                        pa= pa.parentNode;
                    }
                    var F= pa.parentNode;
                    if(what== 'moveup'){
                        var sib= pa.previousSibling;
                        while(sib && sib.tagName!= 'FIELDSET'){
                            sib= sib.previousSibling;
                        }
                        if(sib){
                            move_hidden_selected(sib.getAttribute('id'),"up");
                            F.insertBefore(pa,sib);
                        }
                    }
                    else if(what=='movedown'){
                        var sib= pa.nextSibling;
                        while(sib && sib.tagName!= 'FIELDSET'){
                            sib= sib.nextSibling;
                        }
                        if(sib){
                            move_hidden_selected(sib.getAttribute('id'),"down");
                            F.insertBefore(sib,pa);
                        }
                    }
                    if(sib) who.focus();
                }
                else if(what=="remove"){
                    var answer = confirm ("Do you really want to remove this selected file from the list?");
                    if(answer){
                        var pa= who.parentNode;
                        while(pa && pa.tagName != 'FIELDSET'){
                            pa= pa.parentNode;
                        }
                        remove_hidden_selected(pa.getAttribute('id'));
                        pa.parentNode.removeChild(pa);
                    }
                }
            }
        }
    }
    
    </script>
    </head>
    
    <body>
    
    <form id="form1" action="">
    
    
    <input type="hidden" id="mediaorder" name="mediaorder" value="video--0,widevideo--0,widevideo--1,audio--0" />
    
    <input type="hidden" name="video--0" value="file1.flv" />
    <input type="hidden" name="widevideo--0" value="file2.flv" />
    <input type="hidden" name="widevideo--1" value="file3.flv" />
    <input type="hidden" name="audio--0" value="file4.mp3" />
    
    <fieldset>
    <legend>A List!</legend>
        <div class="container">
            <div class="question">
                <fieldset id="video--0">
                <legend><span class="button_updown"><button id="up" name="up" type="button" class="button_updown" onmouseover="this.className='button_updown_pos_hover'" onmouseout="this.className='button_updown'" value="moveup">/\</button><button id="down" name="down" type="button" class="button_updown" onmouseover="this.className='button_updown_pos_hover'" onmouseout="this.className='button_updown'" value="movedown">\/</button></span><span>file1.flv</span><span class="button_remove"><button id="remove" name="remove" class="button_remove" type="button" onmouseover="this.className='button_updown_neg_hover'" onmouseout="this.className='button_remove'" value="remove">X</button></span></legend>
                    <div class="container">
                        <div class="question">
                            <label>stuff here1</label>
                        </div>
                    </div>
                </fieldset>
                
                <fieldset id="widevideo--0">
                <legend><span class="button_updown"><button id="up" name="up" type="button" class="button_updown" onmouseover="this.className='button_updown_pos_hover'" onmouseout="this.className='button_updown'" value="moveup">/\</button><button id="down" name="down" type="button" class="button_updown" onmouseover="this.className='button_updown_pos_hover'" onmouseout="this.className='button_updown'" value="movedown">\/</button></span><span>file2.flv</span><span class="button_remove"><button id="remove" name="remove" class="button_remove" type="button" onmouseover="this.className='button_updown_neg_hover'" onmouseout="this.className='button_remove'" value="remove">X</button></span></legend>
                    <div class="container">
                        <div class="question">
                            <label>stuff here2</label>
                        </div>
                    </div>
                </fieldset>
                
                <fieldset id="widevideo--1">
                <legend><span class="button_updown"><button id="up" name="up" type="button" class="button_updown" onmouseover="this.className='button_updown_pos_hover'" onmouseout="this.className='button_updown'" value="moveup">/\</button><button id="down" name="down" type="button" class="button_updown" onmouseover="this.className='button_updown_pos_hover'" onmouseout="this.className='button_updown'" value="movedown">\/</button></span><span>file3.flv</span><span class="button_remove"><button id="remove" name="remove" class="button_remove" type="button" onmouseover="this.className='button_updown_neg_hover'" onmouseout="this.className='button_remove'" value="remove">X</button></span></legend>
                    <div class="container">
                        <div class="question">
                            <label>stuff here3</label>
                        </div>
                    </div>
                </fieldset>
                
                <fieldset id="audio--0">
                <legend><span class="button_updown"><button id="up" name="up" type="button" class="button_updown" onmouseover="this.className='button_updown_pos_hover'" onmouseout="this.className='button_updown'" value="moveup">/\</button><button id="down" name="down" type="button" class="button_updown" onmouseover="this.className='button_updown_pos_hover'" onmouseout="this.className='button_updown'" value="movedown">\/</button></span><span>file4.mp3</span><span class="button_remove"><button id="remove" name="remove" class="button_remove" type="button" onmouseover="this.className='button_updown_neg_hover'" onmouseout="this.className='button_remove'" value="remove">X</button></span></legend>
                    <div class="container">
                        <div class="question">
                            <label>stuff here4</label>
                        </div>
                    </div>
                </fieldset>
    
            </div>
        </div>
    </fieldset>
    
    </form>
    </body>
    </html>
    Last edited by Force Flow; May 12, 2008 at 13:41. Reason: code fixes

  5. #5
    Barefoot on the Moon! silver trophy
    Force Flow's Avatar
    Join Date
    Jul 2003
    Location
    Northeastern USA
    Posts
    4,516
    Mentioned
    51 Post(s)
    Tagged
    1 Thread(s)
    found the problem...

    IE doesn't read the value attribute in button elements. It reads the actual text on the button instead.

    Firefox reads the value attribute.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •