0

Currently I have the situation as shown below in the snippet.

But now I want a triangle that is the same on every page. No matter how long the page is. So for example if the page is really long, then the triangle will at one point go out of the screen and there will be no more green background. (as shown here)

example of how the desired result

But the most important thing is that on every page the triangle/angle will be the same. How to do this?

$(document).ready(function() {
  function waitForElement(elementPath, callBack) {
    window.setTimeout(function() {
      if ($(elementPath).length) {
        callBack(elementPath, $(elementPath));
      } else {
        waitForElement(elementPath, callBack);
      }
    }, 300)
  }

  waitForElement("#leftdiv", function() {
    // Initial background height set to be equal to leftdiv
    $('#rightdiv').height($('#leftdiv').height());

    // Initial triangle height set to be equal to leftdiv
    $('#triangle').css('border-top', $('#leftdiv').height() + 'px solid transparent');
  });


  // When window resizes
  $(window).resize(function() {
    // Change height of background
    $('#rightdiv').height($('#leftdiv').height());

    // Change height of white triangle
    $('#triangle').css('border-top', $('#leftdiv').height() + 'px solid transparent');
  });

});
.container-general {
  float: left;
  position: relative;
  background-color: black;
  height: 500px;
  width: 70%;
}

.background-general {
  float: right;
  position: relative;
  /*height is set in javascript*/
  width: 30%;
  background-color: green;
}

#triangle {
  position: absolute;
  height: 0;
  width: 0;
  bottom: 0;
  left: -1px;
  border-left: 10vw solid white;
  border-right: 0px solid transparent;
  /*border-top is set in javascript*/
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container-general" id="leftdiv">
</div>
<div class="background-general" id="rightdiv">
  <div id="triangle"></div>
</div>
Svenmarim
  • 3,633
  • 5
  • 24
  • 56

2 Answers2

3

You don't need JavaScript and jQuery at all for this, as long as you are willing to make minor changes to your markup:

Step 1: Update your markup

  1. Wrap both your .container-general and .background-general with a common parent element
  2. Use display: flex; overflow: hidden; on the parent. This has the effect of stretching the shorter background element to full height of .container-general

Step 2: Determine the fixed angle you want and set aspect ratio

Important note: If you want to keep the angle constant, you will need to know what angle you want. That will require one important trick: you want to keep .background-general the same aspect ratio in all cases, so the angle stays constant. Let's say you want it to be 60° (i.e. Math.Pi / 3): with some math, that means that the height of the .background-general should be this ratio relative to the width:

containerHeightRatioToWidth = Math.tan(Math.PI / 3) = 1.732052602783882...

There is a trick to preserve the aspect ratio: you simply set the padding-bottom of the background element. In this case, you want it to be padding-bottom: 173%); (we don't need absolute precision so we can drop the decimal points).

Here's a handy table on the height (in CSS percentages) you can use:

  • 30deg: padding-bottom: 57%:
  • 45deg: padding-bottom: 100%:
  • 60deg: padding-bottom: 173%:

You can also precalculate the percentage in your browser console by pasting this:

var desiredAngleInDegrees = 60;
Math.tan(Math.PI * desiredAngleInDegrees / 180) * 100

The markup is structured as follows:

└─┬.wrapper
  ├──.container-general
  └─┬.background-general
    └─┬.background-general__background
      ├─::before (triangle)
      └─::after (remaining fill)

To achieve the triangle effect, you have two approaches:

Step 3A: Use clip-path to trim the background element to look like a triangle

clip-path is very widely supported by modern browsers, with a notable exception for IE11 and Edge :/ This should do the trick: clip-path: polygon(100% 0, 0 0, 100% 100%);

.wrapper {
  display: flex;
  overflow: hidden;
}

.container-general {
  background-color: black;
  height: 500px;
  width: 70%;
}

.background-general {
  position: relative;
  width: 30%;
  background-color: green;
  overflow: hidden;
}

.background-general__background {
  position: absolute;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
}

/* Triangle */
.background-general__background::before {
  flex-grow: 0;
  content: '';
  display: block;
  width: 100%;
  padding-bottom: 173%;
  background-color: white;
  clip-path: polygon(0 100%, 0 0, 100% 100%);
}

/* Extra fill */
.background-general__background::after {
  flex-grow: 1;
  content: '';
  display: block;
  background-color: white;
  
  /* Needed to fix subpixel rendering */
  margin-top: -1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
  <div class="container-general" id="leftdiv">
  </div>
  <div class="background-general" id="rightdiv">
    <div class="background-general__background"></div>
  </div>
</div>

Step 3B: Use an inline SVG as background image

For the greater browser compatibility, use an inline encoded SVG and stretch it to 100% width and 100% height of the parent.

We can create a simple 10×10px SVG of the following markup:

<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 10 10">
  <path fill="green" d="M0,0 L10,0 L10,10 z"></path>
</svg>

Note: The preserveAspectRatio="none" is required so that we can freely stretch the SVG beyond its usual aspect ratio. For more information of how the <path>'s d attribute works, see this article: The SVG path Syntax: An Illustrated Guide

Then, all you need is to stuff this short SVG markup as data:image/svg+xml for the background image of the background container, i.e.:

background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 10 10"><path fill="green" d="M0,0 L10,0 L10,10 z"></path></svg>');

See example below:

.wrapper {
  display: flex;
  overflow: hidden;
}

.container-general {
  background-color: black;
  height: 500px;
  width: 70%;
}

.background-general {
  position: relative;
  width: 30%;
  background-color: green;
  overflow: hidden;
}

.background-general__background {
  position: absolute;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
}

/* Triangle */
.background-general__background::before {
  content: '';
  display: block;
  flex-grow: 0;
  width: 100%;
  padding-bottom: 173%;
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 10 10"><path fill="white" d="M0,0 L0,10 L10,10 z"></path></svg>');
  background-size: 100% 100%;
}

/* Extra fill */
.background-general__background::after {
  flex-grow: 1;
  content: '';
  display: block;
  background-color: white;
  
  /* Needed to fix subpixel rendering */
  margin-top: -1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
  <div class="container-general" id="leftdiv">
  </div>
  <div class="background-general" id="rightdiv">
    <div class="background-general__background"></div>
  </div>
</div>
Terry
  • 63,248
  • 15
  • 96
  • 118
  • @Teemu Thanks! I've overlooked that requirement. I've updated my answer accordingly. – Terry Jan 07 '20 at 09:25
  • yes, this works really nice and less complicated then with js haha. But one more thing... I switched the 'clip-path: polygon(0 100%, 100% 100%, 0 0);' because the triangle needs to be white and the green area is for every page a different background image. So when I change the numbers, it works, except for the area underneith the triangle. How do I also cover that up by the polygon? – Svenmarim Jan 07 '20 at 11:32
  • @Svenmarim That's doable by using `display: flex; flex-directon: column;` in the background element, and swapping the background colors around. Then, a pseudo-element should be created to cover the rest of the bottom :) see my updated answer. – Terry Jan 07 '20 at 11:40
  • That works, BUT.... now if there is just a little content on the leftdiv, the page will always have the minimum hight of the total triangle. How to fix this? I am sorry to ask this as well, but this is just out of my knowlegde :/ – Svenmarim Jan 07 '20 at 12:03
  • @Svenmarim If that's the case you need to wrap the child nodes inside `.background-content` with a div and then position the div absolute, so it is taken out of the document flow. Also move the `::after` pseudo-element to the new inner div. – Terry Jan 07 '20 at 12:08
  • I am sorry, I don't understand what you mean. Could you write it as an update on your post? – Svenmarim Jan 07 '20 at 12:12
  • 1
    @Svenmarim See my updated answer. I think that should be it. – Terry Jan 07 '20 at 12:31
  • Unfortunately the 3B option is not working for IE. Any ideas on this? I tried adding/replacing the following things, but still not working for IE. Added the "charset=" in:"url('data:image/svg+xml;charset=utf8," and replaced "<" and ">" for "%3C" and "%3E". – Svenmarim Jan 09 '20 at 03:14
  • 1
    @Svenmarim See https://stackoverflow.com/questions/32870788/css-using-raw-svg-in-the-url-parameter-of-a-background-image-in-ie – Terry Jan 09 '20 at 07:23
  • That did the trick, not only the < and > but encoding everything with this tool: https://meyerweb.com/eric/tools/dencoder/ – Svenmarim Jan 09 '20 at 13:54
1

A simple "border triangle" bind to vw units might do:

body {
  min-height: 2000px;
}

#triangle {
  position: absolute;
  top: 0px;
  right: 0px;
  border-top: 100vw solid #ff0000; /* The height of the triangle */      
  border-left: 30vw solid transparent; /* The width of the triangle */
}
<div id="triangle"></div>

A fiddle to play with.

Teemu
  • 22,918
  • 7
  • 53
  • 106
  • But this way the webpage is always 2000px high and not just the height of the content in the #leftdiv – Svenmarim Jan 07 '20 at 11:00
  • The height of the body in the example is only set to demonstrate the content so that the snippet can be scrolled. You can omit the rule on your own page, where you do have some actual content, it doesn't have an effect to the triangle. – Teemu Jan 07 '20 at 11:02