1

I have the following DOM elements on one horizontal line:

  1. A div containing a line of text; i.e. "Hello world". The width of this div is the same as the containing text.
  2. A button to swap the two divs.
  3. A div containing a listbox. The width of this div is the same as the containing listbox.

With float or position:absolute I can align these elements correctly on one line. However, when the user clicks the swap button, the divs should swap places. I would prefer not to remove the divs from the DOM and adding them again in each other's place. That's because the elements are actually Javascript objects (Google Closure Library components to be more specific) that hold variables and have event handlers attached to them. Is it possible to swap the divs using CSS, without assigning a static width to the divs?

Simplified example:

<div style="position:relative; height:60px;">
  <div style="float:left;">Our Team</div>
  <div style="float:left;"><button onclick="swap();">swap home/away</button></div>
  <div style="float:left;"><select><option>Opponent A</option><option>Opponent B</option></select></div>
</div>
<div style="clear:both;"></div>

When the user clicks the button, the first (our team) and the third (opponent) div should be visually swapped in the browser, preferably without removing the elements from the DOM.

Korneel
  • 1,487
  • 14
  • 40

3 Answers3

4

I would prefer not to remove the divs from the DOM and adding them again in each other's place. That's because the elements are actually Javascript objects (Google Closure Library components to be more specific) that hold variables and have event handlers attached to them.

The best solution is to move the elements in the DOM instead. Doing this will preserve existing event handlers, which means your code should still work:

http://jsfiddle.net/thirtydot/LGGW2/

function swap() {
    var target1 = document.querySelector('#swapMe > :first-child');
    var target2 = document.querySelector('#swapMe > :last-child');

    var parent = target1.parentNode;
    parent.insertBefore(target1, null);
    parent.insertBefore(target2, parent.firstChild);
}​

I'm assuming you already have a reference to the elements you want to swap, so you can replace the document.querySelector lines with whatever is suitable.

After writing this answer, I searched for a more generic "swap" function and found this, which looks good: Swap 2 html elements and preserve event listeners on them

Community
  • 1
  • 1
thirtydot
  • 224,678
  • 48
  • 389
  • 349
1

In your HTML both divs are same (style="float:left;"), no class or id so You can swap only HTML of both divs, i.e.

function swap(e)
{
    e  = e || window.event, target = e.target || e.srcElement;
    var nEl=getElem(target.parentNode, 'next');
    var pEl=getElem(target.parentNode, 'prev');
    var pElHtml=pEl.innerHTML, nElHtml=nEl.innerHTML;
    pEl.innerHTML=nElHtml;
    nEl.innerHTML=pElHtml;
}

function getElem(elem, which) {
    if(which==='next') which='nextSibling';
    else if((which==='prev')) which='previousSibling'; 
    do{
        elem = elem[which];
    }
    while (elem && elem.nodeType != 1);
    return elem;        
}​

DEMO.

The Alpha
  • 143,660
  • 29
  • 287
  • 307
  • 1
    Unfortunately, doing that will mess up bound events. Click "Our Team", then click swap, then click "Our Team" again: http://jsfiddle.net/thirtydot/Wxp9j/1/ – thirtydot Nov 26 '12 at 12:36
  • @thirtydot, you are exactly correct but I thought there's no such an event so I answered that way but thanks again and +1. – The Alpha Nov 26 '12 at 17:26
0

I've got a few solutions to your problem. One or two is to do with your styles. If I was you I'd remove the inline styles (unless added by javascript) and add the same class to all 'floating' elements.

For more updated browsers this will work perfectly:

.floaters{
    display:inline-block;
}

But for older browsers (ie5.5, 6, 7, 8) you'd need to apply:

.floaters{
    display:inline;
    float:left;
    height:60px;/*set a default height*/
}

now for the swap(); function. I'm unsure how your applying this but I'd swap the elements by changing their innerHTML();

function swap(){
    var ele1HTML = document.getElementById('ele1').innerHTML();
    var ele3HTML = document.getElementById('ele3').innerHTML();
    document.getElementById('ele1').innerHTML(ele3HTML);
    document.getElementById('ele3').innerHTML(ele1HTML);
}

But the following code above will require that you add ID attributes to your divs. Here's an example:

<div id="menu">
  <div id="ele1" class="floater">
      Our Team
  </div>
  <div id="ele2" class="floater">
      <button onclick="swap();">swap home/away</button>
  </div>
  <div id="ele3" class="floater">
      <select>
          <option>Opponent A</option>
          <option>Opponent B</option>
      </select>
  </div>
</div>
<div id="anotherDiv"></div>

But don't forget to reapply your styles:

#menu{
    position:relative;
    height:60px; /*not needed for the older browser as I've set the child elements to 60px;*/
}
#anotherDiv{
    clear:both;
}
bashleigh
  • 8,813
  • 5
  • 29
  • 49
  • So you only want to swap the elements once? or change the html within the button as well? – bashleigh Nov 26 '12 at 12:40
  • @Beneto Each time the user clicks the button, the elements are swapped. The elements themselves, and the elements inside them can't be removed from the DOM because there are/can be event handlers attached to them. In client memory, variables are associated to these DOM elements. If you erase the innerHTML of such an element, you would have to reassign those variables and event handlers on every swap action. Also, I don't use inline styles in my production code, but it's a more concise way to show code here. – Korneel Nov 26 '12 at 15:04
  • @Korneel Oh yea I didn't think of that I was just swapping the child elements, never thought of triggering them. – bashleigh Nov 26 '12 at 15:12