1

I try to select all elements on a page except of #content_container and all its descendent subelements and all of its parents. Later I want to hide all the selected elements.

However I already fail with this:

var elems = document.querySelectorAll("body *:not(#content_container *)");

Browser console (chrome) returns: Failed to execute 'querySelectorAll' on 'Document': 'body *:not(#content_container *)' is not a valid selector.

Whereas "body *:not(#content_container)" would not return an error.

How can this be accomplished? note: cant use jQuery.

Edit: Some of you wanted an example. The goal is to keep #content_container AND its content visible on the site, whereas all the other content will be hidden.

<body>
<div class="hide">
    <div class="hide">

    </div>
</div>
<div>
    <div>
        <div class="hide">
            <div class="hide">

            </div>
        </div>
        <div id="content_container">
            <p>  ------- content_container div ------- </p>
            <div>
                    <div>
                            <div>

                            </div>
                    </div>
            </div>
            <div>

            </div>    
        </div>
    </div>
    <div class="hide">

    </div>
</div>
<div class="hide">
    <div class="hide">
        <div class="hide">
            <div class="hide">

            </div>
        </div>
    </div>
</div>
<div class="hide">
    <div class="hide">

    </div>
</div>

haemse
  • 3,971
  • 5
  • 28
  • 40

2 Answers2

2

The only way I can see to do this is to get the subject node, then add all it's siblings to the "don't show" collection, then go up its parent nodes collecting and adding all siblings as you go, stopping at the body.

The following only adds siblings and parent siblings, not the node itself or its immediate ancestors, stopping once the body child nodes are processed. It also only collects element nodes and ignores all others, so empty #text node siblings will still be there, but should not affect the layout. If you want to get rid of those, just delete them.

Click the "Hide stuff" button.

function hideStuff(){
  var el = document.querySelector('#content_container');
  var node, nodes = [];
  
  do {
    var parent = el.parentNode;
    
    // Collect element children
    for (var i=0, iLen=parent.childNodes.length; i<iLen; i++) {
      node = parent.childNodes[i];

      // Collect only sibling nodes that are elements and not the current element
      if (node.nodeType == 1 && node != el) {
        nodes.push(node);
      }
    }

    // Go up to parent
    el = parent;

  // Stop when processed the body's child nodes
  } while (el.tagName.toLowerCase() != 'body');

  // Hide the collected nodes
  nodes.forEach(function(node){
    node.style.display = 'none';
  });
}
<body>
  <div class="hide">hide
    <div class="hide">hide
      
    </div>
  </div>
  <div>
    <div>
      <div class="hide">hide
        <div class="hide">hide
          
        </div>
      </div>
      <div id="content_container">
        <p>  ------- content_container div ------- </p>
        <button onclick="hideStuff()">Hide stuff</button>
        <div>don't hide
          <div>don't hide
            <div>don't hide
              
            </div>
          </div>
        </div>
        <div>don't hide
          
        </div>
      </div>
    </div>
    <div class="hide">hide
      
    </div>
  </div>
  <div class="hide">hide
    <div class="hide">hide
      <div class="hide">hide
        <div class="hide">hide
          
        </div>
      </div>
    </div>
  </div>
  <div class="hide">hide
    <div class="hide">hide
      
    </div>
  </div>
</body>
RobG
  • 142,382
  • 31
  • 172
  • 209
1

This selects then hides everything inside the <body> except #content_container, its children and its parents.

class="hide" is added to the HTML purely to indicate which elements will be hidden, but isn't programmatically useful.

It works by applying the class="hidden" to every element within the <body>, unless it's #content_container or it contains #content_container.
An !important CSS rule then forces the children of #content_container to remain visible.

For demonstration, I have added a 1 second delay before the hiding occurs.

setTimeout( () => { // for demo

  document.querySelectorAll( "body *:not( #content_container )" )
    .forEach( ( v ) => {
      if ( !v.querySelector( "#content_container" ) ) {
        v.classList.add( "hidden" );
      }
    } );

}, 1000 );
body {
  padding: .5rem;
  background: lightgrey;
}
div {
  padding: inherit;
  border: 1px solid black;
  background: lightblue;
}
#content_container {
  background: lightgreen;
  visibility: visible !important;
}

/* to hide the children, remove this rule */
#content_container * {
  visibility: visible !important;
}

.hidden {
  visibility: hidden;
}
<body>
  <div>
    <div class="hide">
      <div class="hide">
        <div class="hide">
          <div class="hide"></div>
        </div>
      </div>
    </div>
    <div>
      <div>
        <div id="content_container">
          <div>
            <div>
              <div></div>
            </div>
          </div>
        </div>
        <div class="hide">
          <div class="hide">
            <div class="hide">
              <div class="hide"></div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>

This solution doesn't maintain the visibility of #content_container's parents.

If #content_container's children should also be hidden, only a minor change to the CSS will fix it.
I have highlighted the CSS that keeps the children visible.

setTimeout( () => { // for demo

  document.querySelectorAll( "body *:not( #content_container )" )
    .forEach( ( v ) => { v.classList.add( "hidden" ); } );

}, 1000 );
body {
  padding: .5rem;
  background: lightgrey;
}
div {
  padding: inherit;
  border: 1px solid black;
  background: lightblue;
}
#content_container {
  background: lightgreen;
  visibility: visible !important;
}

/* to hide the children, remove this rule */
#content_container * {
  visibility: visible !important;
}

.hidden {
  visibility: hidden;
}
<body>
  <div>
    <div class="hide">
      <div class="hide">
        <div class="hide">
          <div class="hide"></div>
        </div>
      </div>
    </div>
    <div>
      <div>
        <div id="content_container">
          <div>
            <div>
              <div></div>
            </div>
          </div>
        </div>
        <div class="hide">
          <div class="hide">
            <div class="hide">
              <div class="hide"></div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>

A far more efficient and simpler alternative to the previous solution is to hide the <body> (and therefore everything else on the page) whilst forcing the visibility of #content_container and its children.

This is a more direct route, faster to apply and easier to reverse if required; my first solution sets a lot of redundant classes.

setTimeout( () => { // for demo

  document.querySelector( "body" ).classList.add( "hidden" );

}, 1000 );
body {
  padding: .5rem;
  background: lightgrey;
}
div {
  padding: inherit;
  border: 1px solid black;
  background: lightblue;
}
#content_container {
  background: lightgreen;
  visibility: visible !important;
}
.hidden {
  visibility: hidden;
}
<body>
  <div>
    <div class="hide">
      <div class="hide">
        <div class="hide">
          <div class="hide"></div>
        </div>
      </div>
    </div>
    <div>
      <div>
        <div id="content_container">
          <div>
            <div>
              <div></div>
            </div>
          </div>
        </div>
        <div class="hide">
          <div class="hide">
            <div class="hide">
              <div class="hide"></div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>
Fred Gandt
  • 4,217
  • 2
  • 33
  • 41