jgthms / bulma

Modern CSS framework based on Flexbox
https://bulma.io
MIT License
49.36k stars 3.95k forks source link

Empty selector not working #3405

Closed roychowdhuryrohit-dev closed 2 years ago

roychowdhuryrohit-dev commented 3 years ago

I want to make the notification disappear when there is no text. How can I make the following work?

.notification {
  &:empty {
    display: none !important;
  }
}

I want to have the similar behaviour in Bulma notification as Bootstrap alert.

Bootstrap Alerts

roychowdhuryrohit-dev commented 3 years ago

The JS method is also not working.

(document.querySelectorAll('.notification') || []).forEach(($notification) => {
      if (!$notification.hasChildNodes()) {
        $notification.parentNode.removeChild($notification);
      }
});
sjoqvist commented 3 years ago

@roychowdhuryrohit-dev Could you provide the HTML as well? Are your sure that your element is empty? Note that even whitespace is considered a child node, making the element non-empty.

roychowdhuryrohit-dev commented 3 years ago

@roychowdhuryrohit-dev Could you provide the HTML as well? Are your sure that your element is empty? Note that even whitespace is considered a child node, making the element non-empty.

I am using Elixir Phoenix root layout.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title><%= assigns[:page_title] || "ARCHETHIC" %></title>
    <link href="https://fonts.googleapis.com/css2?family=Montserrat&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
    <%= csrf_meta_tag() %>
  </head>
  <body>
    <div class="container is-fluid">
      <nav class="navbar is-transparent" role="navigation" aria-label="main navigation">
        <div class="navbar-brand">
            <a class="navbar-item is-size-5 is-uppercase" href="<%= Routes.live_path(@conn, ArchEthicWeb.ExplorerIndexLive) %>">
              ArchEthic
            </a>
            <a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="navbar">
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
            </a>
        </div>
        <div id="navbar" class="navbar-menu">
            <div class="navbar-end">

            <a class="navbar-item" href="<%= Routes.faucet_path(@conn, :index) %>">
                Faucet
            </a>
 <div class="navbar-item has-dropdown is-hoverable" >
              <a class="navbar-link" href="<%= Routes.explorer_path(@conn, :chain) %>">Chains</a>
              <div class="navbar-dropdown is-boxed">
                <a class="navbar-item" href="<%= Routes.live_path(@conn, ArchEthicWeb.OracleChainLive) %>">
                  Oracle
                </a>
                <a class="navbar-item" href="<%= Routes.live_path(@conn, ArchEthicWeb.BeaconChainLive) %>">
                  Beacon
                </a>
              </div>
            </div>
            <a class="navbar-item" href="<%= Routes.live_path(@conn, ArchEthicWeb.NodeListLive) %>">
                Nodes
            </a>
            <div class="navbar-item has-dropdown is-hoverable">
                <a class="navbar-link">
                Governance
                </a>
                <div class="navbar-dropdown is-boxed">
                <a class="navbar-item" href="<%= Routes.live_path(@conn, ArchEthicWeb.CodeViewerLive) %>">
                    Code Viewer
                </a>
                <a class="navbar-item" href="<%= Routes.live_path(@conn, ArchEthicWeb.CodeProposalsLive) %>">
                    Code Proposals
                </a>
                </div>
            </div>
            <a class="navbar-item" href="<%= Routes.static_path(@conn, "/docs/index.html") %>">
                Docs
            </a>
        </div>
    </nav>
    <main class="pt-6">
      <div class="notification is-success is-light" role="notification"><%= get_flash(@conn, :info) %></div>
      <div class="notification is-danger is-light" role="notification"><%= get_flash(@conn, :error) %></div>
      <%= @inner_content %>
    </main>
    </div>
    <script type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
  </body>
</html>
sjoqvist commented 3 years ago

@roychowdhuryrohit-dev I don't know what that get_flash function outputs.

In a context where the divs should be empty, open the browser console and run the following, checking that the result is an array of empty strings. If there's any whitespace at all in the string, it doesn't count as empty by the CSS.

[...document.getElementsByClassName('notification')].map(el => el.innerHTML)

I got some weird results when I tried el.childElementCount on an element with whitespace content. The result was 0, but &:empty wasn't fooled and refused to hide the element until I completely emptied the element. But that doesn't mean that you're experiencing the same thing, of course.

EDIT: Seems like text nodes don't count as elements, which explains the behavior of el.childElementCount. You could try the following and check that the array items are all false.

[...document.getElementsByClassName('notification')].map(el => el.hasChildNodes())
roychowdhuryrohit-dev commented 3 years ago

@roychowdhuryrohit-dev I don't know what that get_flash function outputs.

In a context where the divs should be empty, open the browser console and run the following, checking that the result is an array of empty strings. If there's any whitespace at all in the string, it doesn't count as empty by the CSS.

[...document.getElementsByClassName('notification')].map(el => el.innerHTML)

I got some weird results when I tried el.childElementCount on an element with whitespace content. The result was 0, but &:empty wasn't fooled and refused to hide the element until I completely emptied the element. But that doesn't mean that you're experiencing the same thing, of course.

EDIT: Seems like text nodes don't count as elements, which explains the behavior of el.childElementCount. You could try the following and check that the array items are all false.

[...document.getElementsByClassName('notification')].map(el => el.hasChildNodes())

I got the following console output.

Screenshot 2021-09-14 at 01 25 58

Still getting the blank notification box.

Screenshot 2021-09-14 at 01 30 18
sjoqvist commented 3 years ago

@roychowdhuryrohit-dev This is weird. But the fact that you can't even get rid of the notifications by removing them with jQuery makes it seem like it's not because of Bulma. Your screenshot shows that jQuery should in fact have removed the elements, but I don't know if "is also not working" means that the elements weren't removed or that the visual boxes remain even though the elements are gone. Unfortunately, this is a bit difficult to debug remotely.

Does your CSS style get applied to the elements? (This should say none.)

[...document.getElementsByClassName('notification')].map(el => window.getComputedStyle(el).display)

Does your CSS style work if you remove &:empty?

What happens if you apply the style inline without caring if they're :empty? (You could use the following or edit the elements in the 'Inspect' view.)

[...document.getElementsByClassName('notification')].forEach(el => { el.style.display = 'none'; })

What happens if you remove the elements without caring if they're :empty, and without relying on jQuery? (Using the following code or by doing it manually.)

[...document.getElementsByClassName('notification')].forEach(el => { el.remove(); })

EDIT: Forget what I wrote about jQuery. That's not jQuery. I was confused by the dollar signs.

roychowdhuryrohit-dev commented 3 years ago

@roychowdhuryrohit-dev This is weird. But the fact that you can't even get rid of the notifications by removing them with jQuery makes it seem like it's not because of Bulma. Your screenshot shows that jQuery should in fact have removed the elements, but I don't know if "is also not working" means that the elements weren't removed or that the visual boxes remain even though the elements are gone. Unfortunately, this is a bit difficult to debug remotely.

Does your CSS style get applied to the elements? (This should say none.)

[...document.getElementsByClassName('notification')].map(el => window.getComputedStyle(el).display)

Does your CSS style work if you remove &:empty?

What happens if you apply the style inline without caring if they're :empty? (You could use the following or edit the elements in the 'Inspect' view.)

[...document.getElementsByClassName('notification')].forEach(el => { el.style.display = 'none'; })

What happens if you remove the elements without caring if they're :empty, and without relying on jQuery? (Using the following code or by doing it manually.)

[...document.getElementsByClassName('notification')].forEach(el => { el.remove(); })

Okay so after initially reloading the page, I ran the following in console and got this output which wasn't none.

Screenshot 2021-09-15 at 01 26 43

Then I ran the next two commands in the console and they were able to successfully remove those two visual boxes.

[...document.getElementsByClassName('notification')].forEach(el => { el.style.display = 'none'; })
[...document.getElementsByClassName('notification')].forEach(el => { el.remove(); })

But still the following doesn't work when I'm trying to check for empty child and then remove even without using &:empty there are still empty boxes.

(document.querySelectorAll('.notification') || []).forEach(($notification) => {
      if (!$notification.hasChildNodes()) {
        $notification.parentNode.removeChild($notification);
      }
});

Moreover the bootstrap alert works perfectly with &:empty.

<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
.alert {
    top: 52px;
    width: 417px;
    margin: 0 auto;
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    text-align: center;
    height: 80px;
    line-height: 80px;
    background: rgba(255,229,229,0.84);
    box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.18);
    border-radius: 5px;
    color: #C35A5A;
    padding-top: 10px;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;

    &:empty {
      display: none;
    }

    &.alert-info {
      background: rgba(191, 232, 176, 0.82);
      color: #317931;
      -moz-animation: cssAnimation 5s forwards;
      -webkit-animation: cssAnimation 5s forwards;
      -o-animation: cssAnimation 5s forwards;
      animation: cssAnimation 5s forwards;
      -webkit-animation-fill-mode: forwards;
      animation-fill-mode: forwards;
    }

    @keyframes cssAnimation {
      0%   {opacity: 1;}
      95%  {opacity: 1;}
      100% {opacity: 0;}
    }

    @-webkit-keyframes cssAnimation {
      0%   {opacity: 1;}
      95%  {opacity: 1;}
      100% {opacity: 0;}
    }
}
jgthms commented 3 years ago

What is the actual HTML output?

sjoqvist commented 3 years ago

But still the following doesn't work when I'm trying to check for empty child and then remove even without using &:empty there are still empty boxes.

(document.querySelectorAll('.notification') || []).forEach(($notification) => {
      if (!$notification.hasChildNodes()) {
        $notification.parentNode.removeChild($notification);
      }
});

This doesn't make sense to me. The code works for me. Could you try running it both like this and without the exclamation mark (!) in the if statement? If it works without the exclamation mark, then it means that the browser thinks that the nodes have children (which also explains why the CSS solution doesn't kick in). If it doesn't work in either case, then the code doesn't target the correct elements.

Furthermore, it doesn't make sense for .alert:empty { display: none; } to work when .notification:empty { display: none !important; } isn't. Did you verify that the SCSS is compiled correctly into CSS?

Both the JavaScript and the CSS methods of removing the boxes should be okay if the nodes indeed don't have any children because of how the browser works (which Bulma can't override). Like @jgthms says, we'd need to see the actual HTML output. I'm still wondering if you sometimes end up with whitespace inside the node.

roychowdhuryrohit-dev commented 3 years ago

So I tried removing the ! in the if statement and it didn't show any change and was not able to remove the empty boxes on initial load. Are you able to achieve correct behaviour with the .notification:empty { display: none !important; }?

What is the actual HTML output?


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>ARCHETHIC</title>
<link href="https://fonts.googleapis.com/css2?family=Montserrat&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/app.css"/>
<meta charset="UTF-8" content="NxEeFXAFKiwRRwo7NCc0QRF_FQlKEhADzKlJ1d_IG2eKwwmuDJrJxjfy" csrf-param="_csrf_token" method-param="_method" name="csrf-token">
</head>
<body>
<div class="container is-fluid">
<nav class="navbar is-transparent" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item is-size-5 is-uppercase" href="/explorer">
ArchEthic
</a>
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="navbar">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
    <div id="navbar" class="navbar-menu">
        <div class="navbar-end">

        <a class="navbar-item" href="/faucet">
            Faucet
        </a>
<!--
      TODO: implement the listing of transactions through BeaconChain

          <a class="navbar-item" href="/explorer/transactions">
             Transactions
          </a>

-->

        <a class="navbar-item" href="/explorer/nodes">
            Nodes
        </a>

        <div class="navbar-item has-dropdown is-hoverable">
            <a class="navbar-link">
            Governance
            </a>
            <div class="navbar-dropdown is-boxed">
            <a class="navbar-item" href="/explorer/code/viewer">
                Code Viewer
            </a>
            <a class="navbar-item" href="/explorer/code/proposals">
                Code Proposals
            </a>
            </div>
        </div>

        <a class="navbar-item" href="/docs/index.html">
            Docs
        </a>
    </div>
</nav>
<main class="pt-6">
  <div class="notification is-success is-light" role="notification"></div>
  <div class="notification is-danger is-light" role="notification"></div>

ArchEthic Explorer

Discover the most decentralized and unlimited P2P network

Last beacon chain summary statistics

Last TPS

0.05

Total Transactions

26



<img width="1476" alt="Screenshot 2021-09-14 at 01 30 18" src="https://user-images.githubusercontent.com/24897721/133390795-64346431-6205-4dfc-86ce-e4c9d1f1ac78.png">
sjoqvist commented 3 years ago

So I tried removing the ! in the if statement and it didn't show any change and was not able to remove the empty boxes on initial load.

Sounds like it's not Bulma that causes this. Bulma can't prevent you from removing elements from the DOM.

Are you able to achieve correct behaviour with the .notification:empty { display: none !important; }?

Yes. Here's what I'm doing.

  1. In Chrome, open https://bulma.io/documentation/elements/notification/ (just to pick some page that obviously contains Bulma notifications).
  2. Open the Developer Tools with Ctrl + Shift + J (Linux/Windows mapping).
  3. Open the Elements tab to the left.
  4. Open the Styles tab in the smaller window to the right.
  5. Click the tiny + sign ("New Style Rule").
  6. Paste .notification:empty as the selector and display: none !important as the rule.
  7. Pick any of the notifications in the DOM (for example by right-clicking on the page and selecting "Inspect").
  8. Right-click the <div …> in the Elements view and pick "Edit as HTML".
  9. Remove everything between > and < (including whitespace) so that only <div class="notification [custom classes]"></div> remains.
  10. Click outside of the edit box to save the element and update the DOM.
  11. Look at the page to see that the box is gone.
  12. Click the element under the Elements tab and look in the Styles tab to see that the display rule is now in effect.
  13. Uncheck the checkbox next to the display rule.
  14. Look at the page to see that the box has now reappeared.

You may try everything above (e.g. on another of the notifications on the same page) while leaving whitespace (such as a linebreak) in step 9 to check the difference. The box doesn't disappear in this case.

sjoqvist commented 3 years ago

I've now also done this.

  1. Copy the HTML output that you listed into a new file.
  2. Download https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.3/css/bulma.min.css into the same folder.
  3. Edit the HTML file so that <link rel="stylesheet" href="/css/app.css"/> becomes <link rel="stylesheet" href="bulma.min.css"/>.
  4. Open the file in Chrome (Ctrl + O).
  5. Verify that both the HTML and CSS loaded, by checking that you can see the empty notification boxes.
  6. Open the CSS file and insert .notification:empty { display: none !important; } at the top.
  7. Reload the page and verify that the notification boxes are now hidden.

(Yes, it's working for me.)

sjoqvist commented 3 years ago

@roychowdhuryrohit-dev Have you made any progress with debugging? Could you please check the following?

  1. &:empty isn't valid CSS but SCSS. Are you compiling it correctly, and is the newly compiled file then also served to the browser?
  2. Does the browser download the new stylesheet or has it cached the old one?
  3. Is the rest of the compiled CSS valid? I've seen a case where the browser silently stops parsing CSS when it encounters an error, and this file was in production for months before I found the bug by accident. You can try to move the style to the beginning of the file or validate the entire file using https://jigsaw.w3.org/css-validator/
  4. Does the same thing happen in other browsers?
  5. If nothing else works, could you reduce this to the absolute minimal HTML and CSS that exhibits the problem? From what I can tell, your HTML isn't valid. I haven't looked at it in detail, but I don't think that even <div class="notification is-danger is-light" role="notification"></div> is valid, as I've never seen role="notification".

Regarding the 5th item, were you actually looking for something like role="alert"? Please note that the alert role is intrusive and will cause a screen reader to inform the user immediately. This is equivalent to aria-live="assertive", but you may also try aria-live="polite", which is less intrusive.

roychowdhuryrohit-dev commented 3 years ago

@roychowdhuryrohit-dev Have you made any progress with debugging? Could you please check the following?

  1. &:empty isn't valid CSS but SCSS. Are you compiling it correctly, and is the newly compiled file then also served to the browser?
  2. Does the browser download the new stylesheet or has it cached the old one?
  3. Is the rest of the compiled CSS valid? I've seen a case where the browser silently stops parsing CSS when it encounters an error, and this file was in production for months before I found the bug by accident. You can try to move the style to the beginning of the file or validate the entire file using https://jigsaw.w3.org/css-validator/
  4. Does the same thing happen in other browsers?
  5. If nothing else works, could you reduce this to the absolute minimal HTML and CSS that exhibits the problem? From what I can tell, your HTML isn't valid. I haven't looked at it in detail, but I don't think that even <div class="notification is-danger is-light" role="notification"></div> is valid, as I've never seen role="notification".

Regarding the 5th item, were you actually looking for something like role="alert"? Please note that the alert role is intrusive and will cause a screen reader to inform the user immediately. This is equivalent to aria-live="assertive", but you may also try aria-live="polite", which is less intrusive.

  1. The same method works correctly for the .alert class that I have shared earlier.

I will update this thread with more findings soon. Meanwhile to get a proper idea of what I'm dealing with, you can access the particular file which is available as an open source project.

CSS file link

Here when I add the .alert class it's working fine. But same method doesn't work for Bulma notification component.

sjoqvist commented 3 years ago
  1. The same method works correctly for the .alert class that I have shared earlier.

Just compared the code snippets that you posted earlier. I noted that you had

<p class="alert alert-danger" role="alert">

which is valid HTML, and

<div class="notification is-danger is-light" role="notification">

which is invalid. Hadn't paid attention to that before. Perhaps some browser will refuse to add that style to an invalid element. You may also want to check if any styles are applied differently because of the <p>/<div> difference.

But I'd say that item 1 isn't completely answered because you've compiled the code with the alert class. Just because you've compiled SCSS at some point you can't know that it'll keep working in the future as well. Perhaps you've upgraded the build tools, perhaps the Docker image wasn't refreshed, etc. In Chrome, you can open Developer Tools and go to the Sources tab to ensure that the browser is loading the correct CSS. This will help you verify both that compilation is working and that caching is not an issue.

If I have time later, I might create a VM to look into your project. Not right now, though.

stale[bot] commented 2 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.