3
\$\begingroup\$

The point of this code is to mask a page where the user is editing the page's 'body' content. This is in a CMS. The rest of the page is the website template (i.e. header, index bar etc...). I've covered resizing the window, and the DIV itself growing larger.

I'd love to know if there is a more efficient way to do this. I've used a DIV position: fixed as a mask under the editable DIV but some of our templates have irregular background colours (or images) and I really want to have a transparent editing area so that the experience is as close as possible to the end result.

If it's not apparent, this code creates a DIV whose borders cover the space that the editable DIV does not take up on the document. As the page resizes or the editable DIV changes size, these borders need to change size.

NOTE: The outer container may have overflow: hidden applied (thus the need to append the mask to the body element). The intention of the mask is to prevent clicking on any other area of the page other than the area that is being edited.

var content_builder_element = $("#content_builder");

var mask = $('<div class="content_builder_mask"></div>');

buildMask(content_builder_element, mask, false);

$('body').append(mask);

$(window).resize(function() {
    buildMask(content_builder_element, mask, true);
});

window.setInterval(function() {
    buildMask(content_builder_element, mask, true);
}, 100);

function buildMask(reference, mask, resize) {
    var offset = reference.offset();
    var height = reference.outerHeight(true);
    var width = reference.outerWidth(true);

    // We don't want the width of the masks to mess with the document width
    if (resize) {
        mask.css('borderRightWidth', 0);
        mask.css('borderBottomWidth', 0);
    }

    var document_width = $(document).width();
    var document_height = $(document).height();

    mask.width(width);
    mask.height(height);

    mask.css('borderTopWidth', offset.top);
    mask.css('borderRightWidth', document_width - offset.left - width);
    mask.css('borderBottomWidth', document_height - offset.top - height);
    mask.css('borderLeftWidth', offset.left);
};
body {
  background-color: orange;
}

.content_builder_mask {
  border: 0px solid rgba(0, 0, 0, 0.6);
  position: absolute;
  top: 0;
  left: 0;
}

#somecontainer {
  margin: 50px;
  overflow: hidden;
  }

#content_builder {
  min-height: 200px;
  position: relative;
  z-index: 500;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
  </head>
  
  <body>
    <div id="somecontainer">
      <div id="content_builder" contenteditable="true">
        
      </div>
    </div>
  </body>
</html>
  
	  
 
  

\$\endgroup\$
  • \$\begingroup\$ is the semi-tranparency of the mask a requirement, or can it be fully opaque? \$\endgroup\$ – Pevara Sep 15 '14 at 21:09
  • \$\begingroup\$ Semi-transparent would be good as the intent is to give the user an idea of what their content will look like within the context of their template. Otherwise we'd not need to worry about rendering the template at all because the user wouldn't see it (making this solution moot). \$\endgroup\$ – Brendon Muir Sep 16 '14 at 1:16
2
\$\begingroup\$

It is not entirely clear to me what the goal of that mask exactly is, but I guess you want to bring focus to your content div while editing it (correct me if I'm wrong, perhaps a simple demo would be nice to see what you want to achieve exactly)

If my assumption is correct, I would keep thing a lot simpler, and let css take care of things. This is by far the most performant solution I believe, since browser are obviously quite well optimized for applying css.

You could just apply a ridiculously large box shadow to your focus div, which will obscure the rest of the content, while keeping it's background in tact. No need to alter your DOM and check for resizes or anything. The css would look something like this:

div[contenteditable] {
  box-shadow: 0 0 0 9999px rgba(0,0,0,.8);
}

I've set up a very simple demo to demonstrate (hit the edit button to see the effect, and edit the content to see how it reacts on resize)

$('button').click(function() {
 $('div').first().attr('contenteditable', true);
});
body {
  background: red;
}

div[contenteditable] {
  box-shadow: 0 0 0 9999px rgba(0,0,0,.8);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<h1>Test CMS</h1>
<div>
  Lorem ipsum
</div>
<div>
  Lorem ipsum
</div>
<button>edit</button>

\$\endgroup\$
  • \$\begingroup\$ Thanks Peter, this is a great trick. Unfortunately there's a couple of points that I forgot to mention in the question. Firstly is that a lot of the time the containing DIV for the editor will have overflow:hidden on it which precludes placing anything within it for masking the entire page. The drop shadow in your example also doesn't seem to prevent clicking on any of the surrounding items (at least in Safari) which is the main point of the mask (because the template is essentially rendered live). I'll look at making my code into a snippet (didn't notice it there!) :) \$\endgroup\$ – Brendon Muir Sep 15 '14 at 19:33
  • \$\begingroup\$ Looking forward to the 'complete' question. pointer-events: none is the first thing that comes to mind... \$\endgroup\$ – Pevara Sep 15 '14 at 20:57
  • \$\begingroup\$ Thanks Peter, it should be showing up now with an example. \$\endgroup\$ – Brendon Muir Sep 15 '14 at 21:04
0
\$\begingroup\$

Hmmm... What you say about this solution? In CSS:

html, body, .black-bg {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
    box-sizing: border-box;
}

.black-bg {
    background-color: rgba(0, 0, 0, .5); //opacity 50%
}

In JS:

var bg = $("<div class='black-bg'/>).insertBefore("#editedBlock");

bg.click(function(){
    bg.remove(); //remove from DOM
});
\$\endgroup\$
  • \$\begingroup\$ Unfortunately this would obscure the background behind the #editedBlock. I want this to show through because it could be a solid colour, or a repeating background image of some sort. #editedBlock is transparent and we don't know the background behind it ahead of time. \$\endgroup\$ – Brendon Muir Sep 12 '14 at 19:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.