3

I'd like to enable and disable outlines on all objects on a page and I'm doing that with the following CSS code:

*, *:before, *:after {
  outline:1px dotted red;
}

How would I do that programmatically with JavaScript where I can enable or disable it?

I think I can do something like this for a specific tag but not for *:

document.getElementsByTagName("body")[0].style = "outline:1px dotted red";

Pseudo code to enable outline:

document.getElementByTagName("*").setStyle("outline:1px dotted red");
document.getElementByTagName("*:before").setStyle("outline:1px dotted red");
document.getElementByTagName("*:after").setStyle("outline:1px dotted red");

Pseudo code to disable outline:

document.getElementByTagName("*").setStyle("outline:0px dotted red");
document.getElementByTagName("*:before").setStyle("outline:0px dotted red");
document.getElementByTagName("*:after").setStyle("outline:0px dotted red");
1.21 gigawatts
  • 16,517
  • 32
  • 123
  • 231
  • How about using a `for` loop? – Sebastian Simon Aug 08 '15 at 23:50
  • possible duplicate of [Inject CSS stylesheet as string using Javascript](http://stackoverflow.com/questions/15505225/inject-css-stylesheet-as-string-using-javascript) – Jan Aug 08 '15 at 23:52
  • `getElementByTagName`? Does that even exist? Isn't it `getElementsByTagName` and doesn't it return an array (-like object)? – spender Aug 08 '15 at 23:52
  • @spender Oops. It's pseudocode. I'll update to getElementsByTagName("body")[0]. – 1.21 gigawatts Aug 08 '15 at 23:55
  • It’s only an “array” if it has all the prototype methods of `Array`. It returns a `NodeList` that can be easily converted to an `Array` with ES5: `[].slice.call(nodeList)`, or with ES6: `[...nodeList]`, `Array.from(nodeList)`, etc. Then `map` or `forEach` can be used. – Sebastian Simon Aug 08 '15 at 23:56
  • @Xufox a for loop on all tags? Maybe but * is supposed to be a reference to all elements. I think that would be a last resort. I see your update. Can you post some code in an answer I can try? – 1.21 gigawatts Aug 08 '15 at 23:58
  • @1.21gigawatts With `for` loop I meant `for(var i=0; i – Sebastian Simon Aug 09 '15 at 00:01
  • 2
    Why was this question closed as off topic? It very clearly explains what the author wants to be able to do and three people at least understood it enough to provide a working answer – KJ Tsanaktsidis Aug 11 '15 at 12:20
  • 1
    @KJTsanaktsidis Stack Exchange gives anyone the power to be a moderator and vote to close or put questions on hold. You only need to achieve 3000 points. I could be biased or temperamental and vote to close peoples questions if I wanted. I don't do that but I could and so could others. I would rather someone ask for clarification or suggest an edit than close and a few do that. Since there is no way to moderate the moderators legitimate questions are put on jeopardy regularly. – 1.21 gigawatts Aug 13 '15 at 06:13
  • 1
    Yes I agree. If anything it could possibly be "too broad", but I don't think so either. It certainly doesn't fit the current close reason. – Jan Aug 13 '15 at 08:01

3 Answers3

3

You generally would NOT want to loop through all objects on your page. If we ignore performance issues, there's also the case of actually potentially overwriting existing styles that you want to keep.

Drawing from Inject CSS stylesheet as string using Javascript

var node;
function changeAllElementsOutline(outlineWidth) {
  if (!node) {  
    node = document.createElement('style');
    document.body.appendChild(node);
  }
  node.innerHTML = '*, *:before, *:after { outline:' + outlineWidth + 'px dotted red; }'
}
<div onClick="changeAllElementsOutline(1);">Set outline</div>
<p onClick="changeAllElementsOutline(0);">Remove outline</p>
Community
  • 1
  • 1
Jan
  • 5,688
  • 3
  • 27
  • 44
  • Would the set style limit mentioned here affect this, http://stackoverflow.com/a/15506705/441016. It looks like no since it is replacing the same stylesheet not adding new ones. Just thought I'd mention it tho. – 1.21 gigawatts Aug 09 '15 at 00:46
  • 1
    This just uses the one stylesheet, so unless you already have 32 you're safe. – Jan Aug 09 '15 at 00:51
3

The best answer is going to be to keep the style loaded but make it conditional on there being a class on the body. You can then turn the style on and off by adding/removing the class.

CSS:

body.outline-enabled *, 
body.outline-enabled *:before, 
body.outline-enabled *:after {
    outline: 1px solid red;
}

JavaScript:

function addOutline() {
    document.getElementsByTagName('body')[0].classList.add('outline-enabled');
}

function removeOutline() {
    document.getElementsByTagName('body')[0].classList.remove('outline-enabled');
}

This way, you're delegating the hard work of applying the style to everything that needs it to the browsers CSS engine.

KJ Tsanaktsidis
  • 1,255
  • 1
  • 17
  • 28
  • This is of course the best solution +1 – Jan Aug 09 '15 at 17:45
  • At the time this seemed like the best answer but I realized it only applies if you have the foresight to plan it ahead of time. If you have to dynamically add or remove styles it does not apply. Depending on your use case either answer could be the best. But to match the answer with the question the other answer is more applicable. – 1.21 gigawatts Aug 13 '15 at 06:17
1

You could do something like this:

var elements = document.querySelectorAll('*');
for(var i = 0; i < elements.length; i++) {
  elements[i].style.outline = '1px dotted red';
}

document.querySelectorAll returns an instance of NodeList (which is an array-like), so you can iterate over its elements.

There is no method on NodeList to set the style of its elements.

Benoit
  • 751
  • 5
  • 8