Ben Carey
Ben Carey

Reputation: 16948

Moving Elements in DOM for Responsive Web Design

A Brief Explanation

I have created a responsive website that effectively has three views (desktop,tablet,mobile). Most of the design is changed via CSS using media queries (as it should be for responsive websites). However, part of the design is too complex to simply be manipulated via CSS as the HTML actually needs to be moved around the DOM. I know this doesn't sound too great but I cannot see how else I am meant to replicate this design without moving certain elements within my HTML.

So, with the above in mind, I now plan to write a function that creates custom events for each of the different ‘responsive views’. When each event is fired (in sync with the CSS media queries), it will execute some Javascript to move/animate any elements that cannot be manipulated enough using CSS.

My Question

Is this the best method to go about doing this? If, not, what other options do I have? Are there any existing libraries that I could look at and learn from?

My actual question is; What is the best method of moving elements in the DOM for responsive web design?

Further Explanation

If the above wasn’t clear, then read the following:

Consider these three different views:

Responsive Views

Now consider the code for the first two views:

Desktop

<div id="some_container">
    <nav>
        <ul>
        </ul>
    <nav>
    <p>Some Text</p>
    <!-- The rest of the content -->
</div>

Tablet

<div id="some_container">
    <p>Some Text</p>
    <nav>
        <ul>
        </ul>
    <nav>
    <!-- The rest of the content -->
</div>

Notice the nav tag has been moved below the p tag...

Upvotes: 30

Views: 26300

Answers (10)

Stu Marlow
Stu Marlow

Reputation: 581

These days, to change the position of particular elements in a design, you do not need to move them in the DOM, you just need to change their display order using the CSS order property:

So given the following HTML structure:

<div id="container">
    <nav>
        <ul>
          <li>Item 1</li>
          <li>Item 2</li>
        </ul>
    </nav>
    <p>Some text</p>
    <!-- The rest of the content -->
</div>

as long as your container is a flex element, you can manipulate the display order of its children (in a media query if you wish) by giving one or more of them an order property. This has a zero-based value, so '1' will placed it second in the order:

#container
{
  display: flex;
  flex-direction: column;
}

@media (max-width: 400px)
{
  nav
  {
    order: 1;
  }
}

On a screen smaller than 401px, you would see this:

Some text

  • Item 1
  • Item 2

Upvotes: 3

Johnattan Tibocha
Johnattan Tibocha

Reputation: 33

If you don't want to make javascript, use grid technique.

<div class="main_container">
    <nav class="grid_nav">
        <ul>
        </ul>
    <nav>
    <div class="grid_text">
        <p>Some Text</p>
    </div>
    <!-- The rest of the content -->
</div>

.main_container{
  display: grid;
  @media screen and (max-width: 500px) { //mobile
    grid-template-rows:    1fr 1fr;
    grid-template-areas: "gridnav"
                         "gridtext";
  }
  @media screen and (min-width: 500px) {//web
    grid-template-rows:    1fr 1fr;
    grid-template-areas: "gridtext"
                         "gridnav";
  }
}

.grid_nav{
  grid-area: gridnav;
}
.grid_text{
  grid-area: gridtext;
}

But is only when the movements are inside of the "main_container"

regards...

Upvotes: 2

crumdev
crumdev

Reputation: 1859

I am doing this exact thing right now with navigation that I need to move into the header for desktop and just below the header for a hamburger menu on mobile. I have an event listener on the window being resized that calls a function that checks screen size and passes it to my toggleNavigation function. I have this separate because I have a few other functions aside from that that get called in the checkBreakPoint that are not listed. Then to make sure that it is not constantly moving my dom or re-appending it I first check what parent container it is in and only move it if it is not in the one I expect for that screensize. My project requires that it not have any external dependencies so this is just done in plain Javascript and then I use media queries to style the elements based on screen size.

var mainNav = document.getElementById('rww_main_nav'); //element I want to move
var tieredHeader = document.getElementById('rww_tiered_header'); //where it should be on mobile
var branding = document.getElementById('rww_branding'); //where it should be on desktop

window.addEventListener("resize", checkBreakPoint);

function checkBreakPoint() {
        //width of viewport
        var w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
        toggleNavLocation(w);
}

function toggleNavLocation(screensize) {
    if (screensize > 999) {
        if (mainNav.parentElement.id === 'rww_tiered_header') {
            branding.appendChild(mainNav);
        }
    } else {
        if (mainNav.parentElement.id === 'rww_branding') {
            tieredHeader.appendChild(mainNav);
        }
    }
}

Upvotes: 0

Patrick Lines-Mattei
Patrick Lines-Mattei

Reputation: 89

This may introduce a whole new set of problems, but could you use position: absolute on the elements set to move, and have them reposition around the page based on the size of the screen?

Upvotes: -1

Thomas Higginbotham
Thomas Higginbotham

Reputation: 1792

Creating duplicate content to solve responsive design challenges is not a best practice. It makes the markup harder to maintain (especially when another developer takes over the project and doesn't realize it has to be updated in more than one location), it increases file size (if file size wasn't important, we wouldn't minify our CSS/JS or GZIP our HTML), and assistive technologies and other web agents that don't take CSS into account see it as repeated content.

It's just messy, and I don't see how anyone would consider it a best practice.

While JavaScript isn't ideal either, I see no issue if it is used as a form of progressive enhancement. I recently found myself using it often enough that I created a jQuery plugin for it: https://github.com/thomashigginbotham/responsive-dom

Sample usage:

var $mainNav = $('.main-nav');

$mainNav.responsiveDom({
    appendTo: '.sidebar',
    mediaQuery: '(min-width: 600px)'
});

Upvotes: 2

jasperjohnny
jasperjohnny

Reputation: 121

The enquire.js library solves this problem by allowing you to fire JS functions in response to CSS media queries. So you could run your detach() and append() logic according to different screen sizes. It is lightweight to boot.

See: http://wicky.nillia.ms/enquire.js/

Upvotes: 6

James Ellis-Jones
James Ellis-Jones

Reputation: 3092

I've just written a jQuery plugin which does exactly this, have a look here. Basically it automatically builds a series of layouts in terms of rows and columns, which are then used for the different views as you show above

Upvotes: 0

Cerbrus
Cerbrus

Reputation: 72857

You can try this:

Assuming this HTML:

<div id="some_container">
    <nav id="n1">
        <ul>
            <li>1</li>
            <li>2</li>
        </ul>
    </nav>
    <p>Some Text</p>
    <nav id="n2">
        <ul>
            <li>1</li>
            <li>2</li>
        </ul>
    </nav>
</div>

You will only need the following css:

#n1{
    display:none;
}

@media only screen 
and (min-width : 600px) {
    #n1{
        display:block;
    }
    #n2{
        display:none;
    }
}

Fiddle example

This basically toggles which of the two navigation sections you see, depending on the screen size. It has the disadvantage of duplicated html in your source (the difference in the amount of data is really negligible), but you won't need JavaScript to get the effect, and JS disabled devices will only show one ul.

The great thing about this way is that it's very scale-able. Need this "effect" on a different page? You'll only have to edit [u]that[/u] page. No messing around with JavaScript, hard-coding new classes / cases.

Upvotes: 8

Liftoff
Liftoff

Reputation: 25392

I would suggest you use .insertAfter() and .resize()/.ready() to move the elements based on the page size:

$(window).resize(){
    resize();
}

$(document).ready(){
    resize();
}

function resize(){
    if($(window).width() < 480)
    {
        //Mobile
        $("#some_container nav").insertAfter("#some_container p");
    }
    else if($(window).width() < 800)
    {
        //Tablet
        $("#some_container nav").insertAfter("#some_container p");
    }
    else
    {
        //Desktop
        //Leave original layout
    }
}

Upvotes: 6

Bhojendra Rauniyar
Bhojendra Rauniyar

Reputation: 85545

There are many methods like appendTo

//check for the window width
if($(window).width() < 700){
  //append to the p  
  $('#some_container nav').appendTo('#some_container p');
}

Upvotes: 2

Related Questions