change stylesheet by button click for specific element only

2.4k Views Asked by At

So, this question is based on a piece of homework that I'm currently working on. I was provided with an HTML document and two stylesheets. The HTML document is really simple:

<html>
<head>
   <title>DOM-scripting: change style</title>
   <link rel="stylesheet" href="style/style1.css" id="stylesheet"/>
</head>
<body>
    <nav class="chooseStyle">
        <input type="button" id="style1Button" value="Style 1"/>
        <input type="button" id="style2Button" value="Style 2"/>
    </nav>
    <article>
        <h1>Style change using DOM-scripting</h1>
        <p> sample text </p>
        <p> sample text </p>
        <p> sample text </p>
    </article>
    <nav class="links">
        <ul>
            <li><a href="link">school name</a></li>
            <li><a href="link">school name</a></li>
       </nav>

    <script src="scriptToUse.js"></script>
</body>
</html>

style1.css consists of the following:

body {
    background-color: #676632;
}

input[type="button"] {
    color: #F5F265;
    background-color: #9B9302;
}

article {
    margin-left: 150px;
    margin-right: 150px;
    text-indent: 20px;
    color: #C9C892;
}

h1 {
    color: #EEF1BA;
    text-align: center;
}

a {
    color: #A1FA56;
    text-decoration: none;
    font-weight: bold;
}

ul {
    padding: 0;
}

li {
    list-style-type: none;
}

Style2.css consists of the following:

body {
    color: #EEEEAA;
}

input[type="button"] {
    color: #9B9302;
    background-color: #F5F265;
}

article {
    margin-left: auto;
    margin-right: auto;
    text-align: justify;

    color: #A3A163;
}

h1 {
    color: #AAAA66;
    text-align: left;
}

a {
    color: #CDCF9B;
}

ul {
    padding: 0;
}

li {
    list-style-type: none;
}

The first part of the exercise states that I need to create a javascript file which allows the stylesheet for the webpage to change according to the button that was clicked. The first button refers to the first stylesheet and the second refers to the second stylesheet. I figured this out fairly easy and everything works fine.

However, the second part of the exercise states the following: "Now edit the javascript file so that only the style of the article is changed when the buttons are clicked. DO NOT edit the CSS file." Meaning that the href for the stylesheets should still be changed, like in the first part, but now it may only affect the style of the article element instead of the style of the whole page.

I have been searching for related questions for the past hour but nothing seems to really provide a solution. I have read some questions stating that it is impossible to apply a stylesheet only to a specific element without changing anything in the stylesheet itself. Keep in mind that I am only in my first year of college and have only just begun learning javascript, meaning that a solution should not be that hard to find. I guess?

The Javascript code I wrote so far:

'use strict';

const handleLoad = () => {
    let style1 = document.getElementById('style1Button');
    let style2 = document.getElementById('style2Button');
    style1.addEventListener('click', handleClick);
    style2.addEventListener('click', handleClick);
};

const handleClick = (event) => {
    if(event.target.value === "Style 1") {
        document.getElementById("stylesheet").href = "style/style1.css";
        // should only change article style 
        // currently still referring to the whole document
    } else {
        document.getElementById("stylesheet").href = "style/style2.css";
        // should also only change article style
        // also still referring to the whole document
    }
};

window.addEventListener('load', handleLoad);

Is there any way I could block the CSS rules for all elements except the article element? Or maybe change the classes or ID's for the elements that should not be affected?

Thanks a lot in advance :)


This is what finally solved it. (code is probably not as compact and correct as it could be but it works so I'm more than happy :D):

'use strict';

const handleLoad = () => {
    let style1 = document.getElementById('style1Button');
    let style2 = document.getElementById('style2Button');
    style1.addEventListener('click', handleClick);
    style2.addEventListener('click', handleClick);
    makeShadow();
};

const handleClick = (event) => {
    let article = document.querySelector('article');
    let shadow = article.shadowRoot;

    let style = event.target.value === "Style 1" ? "style/style2.css" : "style/style1.css";

    for(let node of shadow.childNodes) {
        if(node.hasAttribute('href')){
            node.href = style;
        }
    }
};

const makeShadow = () => {
    let article = document.querySelector('article');
    let articleClone = makeClone(article);

    let shadow = article.attachShadow({mode:'open'});

    let link = document.createElement('link');
    link.rel = "stylesheet";
    link.href = 'style/style1.css';

    shadow.appendChild(articleClone);
    shadow.prepend(link);
};

const makeClone = (article) => {
    let clone = article.cloneNode();
    clone.innerHTML = article.innerHTML;
    return clone;
};

window.addEventListener('load', handleLoad);
3

There are 3 best solutions below

2
AudioBubble On BEST ANSWER

In This case we can make use of shadowRoot as an encapsulation for styles.

The idea is like adding a css file which contains a ton of styles to a page which only have the article element, obviously the other styles won't have any effect.

We can do the same using a shadowRoot put the article in it's own little world and add a file which contains more styles.

The code is heavily commented. If you have any question leave a comment :)

First on load we prepare the shadow Element:

const handleLoad = () => {

    // Old Code for button handlers still needed
    let style1 = document.getElementById('style1Button');
    let style2 = document.getElementById('style2Button');
    style1.addEventListener('click', handleClick);
    style2.addEventListener('click', handleClick);


    // New Code Below

    // Select the article from the page 
    const article = document.querySelector('article');
    // Create an empty div Element to work as a bucket
    const placeholderElement = document.createElement('div');
    // set it's id attribute to select it later
    placeholderElement.setAttribute('id', 'placeholderElementShadowRot')
    // replace the article with our div in the document
    // the article element is not completly lose at this point
    // we still have in the variable article
    article.replaceWith(placeholderElement);
    // attache a shadow to the div element
    const shadow = placeholderElement.attachShadow({ mode: 'open' });
    // sets the shadow's innerHTML equal to article outerHTML
    shadow.innerHTML = article.outerHTML;


    // create a link
    const link = document.createElement('link');
    // set it's href attribute to the first style file
    link.setAttribute("href", 'style1.css');
    // set the rel attribute
    link.setAttribute("rel", "stylesheet");
    // add the link to the shadow
    shadow.appendChild(link)
};

And on button click

const handleClick = (event) => {
    // Select our div we created before using the id
    const placeholderElement = document.querySelector('#placeholderElementShadowRot');
    // since we already attached a shadow 
    // we can use it through the property .shadowRoot
    const shadow = placeholderElement.shadowRoot;

    // based on which button we add the relevant style file
    if (event.target.value === "Style 1") {
        shadow.querySelector('link').setAttribute("href", "style1.css")
    } else {
        shadow.querySelector('link').setAttribute("href", "style2.css")
    }
};

Note: You have a typo in your JS, The handler is called handleclick lower case click but you're adding an uppercase Click handleClick to addEventListener

4
CanUver On

Very very simple example for you. You said it was new in Javascript. That's why I'm giving you an example that's so easy to understand. In fact, a more logical solution can be produced by looping around the buttons, figuring out which button they are on, defining the elements that need to be changed according to it, but this is the simplest example at this stage. You'll have to investigate and learn for yourself. Good Luck!

const style1Button = document.getElementById("style1Button");
style1Button.addEventListener("click", styleArticle);

function styleArticle(e) {
  let article = document.getElementById("article"); // full article
  // with querySelector, you can access the elements in the main element in this way.
  article.querySelector(".article-title").style.color = "red";
  article.querySelector(".text1").style.color = "blue";
  article.querySelector(".text2").style.color = "orange";
  article.querySelector(".text3").style.color = "gray";
}
<nav class="chooseStyle">
  <input type="button" id="style1Button" value="Style 1" />
  <input type="button" id="style2Button" value="Style 2" />
</nav>
<article id="article">. 
  <!-- you can add a class or id to each of them to get specific with javascript. -->

  <h1 class="article-title">Style change using DOM-scripting</h1> 
  <p class="text1"> sample text </p>
  <p class="text2"> sample text </p>
  <p class="text3"> sample text </p>
</article>
<nav class="links">
  <ul>
    <li><a href="link">school name</a></li>
    <li><a href="link">school name</a></li>
</nav>

Updated Answer :

I think you just made a letter mistake.

 const handleLoad = () => {
      let style1 = document.getElementById('style1Button');
      let style2 = document.getElementById('style2Button');
      style1.addEventListener('click', handleClick);
      style2.addEventListener('click', handleClick);
    }

    //handleclick to --- handleClick
    const handleClick = (event) => { 
      if (event.target.value === "Style 1") {
        document.getElementById("styleSheet").href = "style/style.css";
        // should only change article style 
        // currently still referring to the whole document
      } else {
        document.getElementById("styleSheet").href = "style/style2.css";
        // should also only change article style
        // also still referring to the whole document
      }
    };

    window.addEventListener('load', handleLoad);
2
Stackerexp On

You can select elements with javascript and style them without changing a stylesheet.

There are different ways to do this, but if you cannot alter this HTML file this should do the trick for changing the text color inside the article element:

document.querySelector('article').style.color = 'red';

You can style other things than the color and you can also select the H1 or paragraph elements inside of the article using querySelector but i'll let you figure that out ;)