Nrc
Nrc

Reputation: 9787

The width of a div. It changes dinamically

I want to set the width of .center. I have different rows some with a button, others without. As it loads dynamically, the width of .center should be different in each row.

In the example .center in the first row should be centerWidth2
.center in the second row should be centerWidth1

This is how I want it to look. The question is I do not know when .right is visible or not. (So, I want .center to be shorter when .right is visible and do it automatically.)

var centerWidth1 =  $('#wrap').width();
var centerWidth2 =  $('#wrap').width()-55;

$("#first").css('width', centerWidth2);
$("#second").css('width', centerWidth1);
#wrap {
	position: relative;
	margin: 20px auto;
	width: 80%;
	background-color: red;
}
	
.row {
	position: relative;
	height: 30px;
	margin-bottom: 10px;
}
	
.center {
	float:left;
	min-height: 30px; line-height: 30px;
	display: inline-block;
	text-align: center;
	background-color: blue;
}

.right {
	float:left;
	width: 50px; height: 30px; line-height: 30px;
	display: inline-block;
	text-align: center;
	margin-left: 5px;
	border:0;
	background-color: grey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<div id="wrap">
	<div class="row">
		<div class="center" id="first">center</div>
		<div class="right">right</div>	
	</div>
	<div class="row">
		<div class="center" id="second">center</div>
	</div>
</div>

Upvotes: 1

Views: 115

Answers (2)

sketchugo
sketchugo

Reputation: 26

Since you say you do not know if there's gonna be a .right element or not, I presume you're planning a function for the users to do something and that element is gonna appear or vanish from that row. That said, my suggestion is to keep that element in the document, but toggle between being part of the page flow, or not. How do we do that? with display: none, which is also the value that is gonna be evaluated as false by our .is(":visible"), because any other, such as visibility: hidden or oppacity: 0.0 is gonna be evaluated as true, since they're still in the page flow although you can't actually see them. The script I'm giving you also works if you totally remove the element, since it doesn't find any to evaluate, but I think it's nicer this way.

Here's an example with four rows, with alternate status on the .right element just to visualize the final result:

<!DOCTYPE html>
<html>
    <head>
        <title>Dynamic center</title>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
    </head>
    <body>
        <div id="wrap">
            <div class="row">
                <div class="center">center</div>
                <div class="right" style="display:block">right</div>    
            </div>
            <div class="row">
                <div class="center">center</div>
                <div class="right" style="display:none">right</div>    
            </div>
            <div class="row">
                <div class="center">center</div>
                <div class="right" style="display:block">right</div>    
            </div>
            <div class="row">
                <div class="center">center</div>
                <div class="right" style="display:none">right</div>    
            </div>
        </div>
    </body>
</html>

Now, the problem you're having is because you're just trying to work with a class as if it was an id. Remember that any change you do to any given class, affects every element that has it, and it will obey that last command it is given. To accomplish what you're requiring, using only the class name, you're gonna have to iterate element by element under that class name and modify it individually:

$(function() {

    //rowCount is counting the number of rows in the document
    var rowCount = $(".row").length;
    var centerWidth1 =  $('#wrap').width();
    var centerWidth2 =  $('#wrap').width()-55;

    //this for loop iterates through all the rows, one by one, evaluating whether the "right" class element inside it is hidden or visible, and using your variables centerWidth1 and 2, sets the "center" width accordingly
    for (let i = 0; i <= rowCount; i++) {
        if ($(".row").eq(i).children().eq(1).is(":visible")) {
            $(".row").eq(i).children().eq(0).width(centerWidth2);
        } else {
            $(".row").eq(i).children().eq(0).width(centerWidth1);
        }
    }
});

Also, just a minor observation on the css: set the box-sizing: border-box to avoid any issues in the interface if you add any borders. And it is not necessary, I'd even dare to say it's problematic for what you're trying to do, to set inline-block on these particular elements, so you can just get rid of that.

.div {
    box-sizing: border-box;
}

#wrap {
    position: relative;
    margin: 20px auto;
    width: 80%;
    background-color: red;
}

.row {
    position: relative;
    height: 30px;
    margin-bottom: 10px;
}

.center {
    float: left;
    min-height: 30px;
    line-height: 30px;
    text-align: center;
    background-color: blue;
}

.right {
    float: left;
    width: 50px;
    min-height: 30px;
    line-height: 30px;
    text-align: center;
    margin-left: 5px;
    border:0;
    background-color: grey;
}

Try that and let me know how it works.

EDIT - using flex:

//No Javascript or jQuery needed
.div {
    box-sizing: border-box;
}

#wrap {
    position: relative;
    margin: 20px auto;
    width: 80%;
    background-color: red;
}

/*Here in .row we set the flex display, so the elements inside are elastic*/
.row {
    position: relative;
    height: 30px;
    margin-bottom: 10px;
    display: flex; 
}

/*Here in .center we set it's default width to 100% to cover the whole container,
and by setting flex-shrink to 1, we're telling it to shrink by one the equivalent
amount of space that any extra items are causing the row to break into another line*/
.center {
    float: left;
    width: 100%;
    min-height: 30px;
    line-height: 30px;
    text-align: center;
    background-color: blue;
    flex-shrink: 1;
}

.right {
    float: left;
    width: 50px;
    min-height: 30px;
    line-height: 30px;
    text-align: center;
    margin-left: 5px;
    border:0;
    background-color: grey;
}
<!DOCTYPE html>
<html>
    <head>
        <title>Dynamic center</title>
    </head>
    <body>
        
<div id="wrap">
    <div class="row">
        <div class="center">center</div>
        <div class="right" style="display:block">right</div>    
    </div>
    <div class="row">
        <div class="center">center</div>
        <div class="right" style="display:none">right</div>    
    </div>
    <div class="row">
        <div class="center">center</div>
        <div class="right" style="display:block">right</div>    
    </div>
</div>
    </body>
</html>

Again, this will work also if you actually remove the .right element as well as if you keep it in the document and use display: none. Run the snippet to see.

Upvotes: 1

WizardCoder
WizardCoder

Reputation: 3461

To achieve your solution I have used .each() to loops through the rows. I then check each .row to see if the .right element exists using right.length > 0>. It it does exist in that .row I subtract the width of .right element with the width of #wrap, and apply it to .center. If .right doesn't exist in that row then I just apply the width of #wrap.

P.S. I have also compensated for the margin-left:5px; on .right by using right.css('marginLeft').replace('px', '') to get the margin left value and subtracting it from the width.

I would recommend just using flex though. (See next example after the one below)

// Get the outer width of #wrap
var centerWidth =  $('#wrap').outerWidth();

// Loop through each .row
$('.row').each(function() {
  // Attempt to get the .right element in currently looped .row
  var right = $(this).find('.right');
  // Assign the outer width of #wrap to the width variable
  var width = centerWidth;
  // Check if the .right element exists in currently looped .row
  if(right.length > 0) {
    // If the .right element exists subtract the outer width and the margin left value of .right from the outer width of #wrap and assign the value to the width of .center
    width = parseFloat(width - right.outerWidth() - right.css('marginLeft').replace('px', ''));
  }
  // Otherwise assign the outer width of #wrap to .center
  $(this).find('.center').css('width', width);
});
#wrap {
	position: relative;
	margin: 20px auto;
	width: 80%;
	background-color: red;
}
	
.row {
	position: relative;
	height: 30px;
	margin-bottom: 10px;
}
	
.center {
	float:left;
	min-height: 30px; line-height: 30px;
	display: inline-block;
	text-align: center;
	background-color: blue;
}

.right {
	float:left;
	width: 50px; height: 30px; line-height: 30px;
	display: inline-block;
	text-align: center;
	margin-left: 5px;
	border:0;
	background-color: grey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<div id="wrap">
	<div class="row">
		<div class="center" id="first">center</div>
		<div class="right">right</div>	
	</div>
	<div class="row">
		<div class="center" id="second">center</div>
	</div>
</div>


Alternative Flex Solution

This is a much simpler solution and uses a lot less processing power.

/* FLEX CSS START */
.row {
  display:flex;
}
.center {
  flex-grow:1;
}
.right {
  flex-shrink:1;
}
/* FLEX CSS END */
#wrap {
  margin: 20px auto;
  width: 80%;
  background-color: red;
}

.row {
  height: 30px;
  margin-bottom: 10px;
}

.center {
  min-height: 30px; 
  line-height: 30px;
  text-align: center;
  background-color: blue;
}

.right {
  width: 50px; 
  height: 30px; 
  line-height: 30px;
  text-align: center;
  margin-left: 5px;
  background-color: grey;
}
<div id="wrap">
	<div class="row">
		<div class="center" id="first">center</div>
		<div class="right">right</div>	
	</div>
	<div class="row">
		<div class="center" id="second">center</div>
	</div>
</div>

Upvotes: 2

Related Questions