Search code examples
htmlrandomz-indexoverlapping

div overlapping doesn't work every time


I have a single page that contains a one-field user input form in the middle of it + 15 randomly positioned divs that draw their content from a database. I have managed to get them not to overlap with each other, but couldn't do it for the form div in the middle. So I played around with z-index to get them to hide behind it, if they happen to overlap.

What I notice is that they don't always fall behind it. Sometimes they do hide behind the input field, while other times they appear in front of it.

This is the code I have so far:

In the HTML code I have replaced the dynamic data php code with hard coded text, just for the sake of presenting functionality.

$(window).load(function(){
var maxSearchIterations = 15;
var min_x = 50;
var max_x = 900;
var min_y = 0;
var max_y = 700;
var filled_areas = [];

function calc_overlap(a1) {
    var overlap = 0;
    for (i = 0; i < filled_areas.length; i++) {

        var a2 = filled_areas[i];

        // no intersection cases
        if (a1.x + a1.width < a2.x) {
            continue;
        }
        if (a2.x + a2.width < a1.x) {
            continue;
        }
        if (a1.y + a1.height < a2.y) {
            continue;
        }
        if (a2.y + a2.height < a1.y) {
            continue;
        }

        // If there is an intersection, calculate it.
        var x1 = Math.max(a1.x, a2.x);
        var y1 = Math.max(a1.y, a2.y);
        var x2 = Math.min(a1.x + a1.width, a2.x + a2.width);
        var y2 = Math.min(a1.y + a1.height, a2.y + a2.height);

        var intersection = ((x1 - x2) * (y1 - y2));

        overlap += intersection;

        
    }

    return overlap;
}

function randomize() {

    filled_areas.splice(0, filled_areas.length);

    var index = 0;
    $('.word').each(function() {
        var rand_x = 0;
        var rand_y = 0;
        var i = 0;
        var smallest_overlap = 9007199254740992;
        var best_choice;
        var area;
        for (i = 0; i < maxSearchIterations; i++) {
            rand_x = Math.round(min_x + ((max_x - min_x) * (Math.random() % 1)));
            rand_y = Math.round(min_y + ((max_y - min_y) * (Math.random() % 1)));
            area = {
                x: rand_x,
                y: rand_y,
                width: $(this).width(),
                height: $(this).height()
            };
            var overlap = calc_overlap(area);
            if (overlap < smallest_overlap) {
                smallest_overlap = overlap;
                best_choice = area;
            }
            if (overlap === 0) {
                break;
            }
        }

        filled_areas.push(best_choice);

        $(this).css({
            position: "absolute",
            "z-index": index++
        });
        $(this).animate({
            left: rand_x,
            top: rand_y
        });

    });
    return false;
}

randomize();
});
      #gimme_secrets {
      	margin-top: 20%;
		position: relative;
		
    }

    .word {
    position: absolute;
    font-size: 30px;
    background-color: rgba(255,255,255,0.5);
    opacity: 0.3;
    z-index:9999;
}
    .searchbox {
        cursor: auto;
 
        width: 500px;
        height: 30px;
        background-color : #d1d1d1; 
    }
    
#gimme_secrets{
	z-index : 10;
}

html, body {
 margin: 0; 
 height: 100%; 
 overflow: hidden
}
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <title>Testing random words.</title>
  
  <script type='text/javascript' src='//code.jquery.com/jquery-1.8.3.js'></script>
  <script language="javascript" type="text/javascript" src="randomizer.js"></script>
  
  <link rel="stylesheet" type="text/css" href="style.css">
</head>

<body>
	
	<form action="process.php" method="post" accept-charset="utf-8">
		<div id="gimme_secrets" align="center">
			<div class="secret_label">Gimme gimme gimme?</div>
			<input class="tag searchbox" type="text" name="Secret"/><br/>
			<input class="button" type="submit" value="Share It!" /><br/>
			<div>(This is completely, utterly anonymous!)</div>
		</div>
	</form>

	<!-- divs that will be randomly placed. --> 
  <div id="word0" class="word">This is just a test.1</div>
<div id="word1" class="word">This is just a test.2</div>
<div id="word2" class="word">This is just a test.3</div>
<div id="word3" class="word">This is just a test.4</div>
<div id="word4" class="word">This is just a test.5</div>
<div id="word5" class="word">This is just a test.6</div>
<div id="word6" class="word">This is just a test.7</div>
<div id="word7" class="word">This is just a test.8</div>
<div id="word8" class="word">This is just a test.9</div>
	
	
</body>
</html>

Any help is appreciated. Thank you in advance.


Solution

  • I don't see the problem in the working example given (i.e. the words don't overlap the search for me, but you do say the problem is intermittent), but I'm assuming that something with the javascript is setting z-indices higher than the search box which is causing the problem. The z-index on the serach box is only 10, which is pretty low.

    I'd suggest this as a solution:

    <div class='master-container'>
        <div class='background-with-moving-text'>
            <!-- all moving text elements -->
        </div>
    
        <div class='search-container'>
            <!-- all search box elements -->
        </div>
    </div>
    
    .master-container {
        position: relative; 
    }
    
    .background-with-moving-text {
        left: 0;
        position: absolute;
        top: 0;
        z-index: 0;
    }
    
    .search-container {
        left: 0;
        position: absolute;
        top: 0;
        z-index: 1;
    }
    

    Because of the nature of how z-index works the a z-index set on a child element can never overlay an element in a sibling element where the sibling element has a higher z-index than the original parent element. So, by setting the words container to 0 and keeping all words within it; and the search container to 1 and again keeping all related elements within that we can guarantee that no elements from the words container will overlap those in the search container.

    CSS Tricks explains this better than me, I'm sure!

    Here's a JSFiddle that demonstrates. There's a few more styles in there, to add colours and position things in a simulated fashion, but we can see that all of the words with z-index set to 10 are behind everything in the search container, even though the z-index is set to 1 for all those elements.

    Finally, the master container that has them both in needs position to be set to something other than static (the default css position value if none is set) in order for the positioning of internal elements to work.