How to Hide heading above a task query when query yields no result?

Things I have tried

I have searched the forum, the Tasks plugin manual as well as the plugin Q&As

What I’m trying to do

I have the following task queries:

### Overdue
```tasks
not done
due before today
```
### Due today
```tasks
not done
due today
hide due date
```
### Scheduled for today
```tasks
not done
scheduled today
hide scheduled date
```
### Starting today
```tasks
not done
has start date
starts on today
hide start date
```

Works fine. But if one of the queries is empty (e.g. when there are no tasks due today), I would like the respective heading (as well as the “0 tasks” quesry result) to be hidden.

I think I could do this if I’d query the tasks using dataviewjs by simply adding an if-clause, but I prefer the tasks queries. But from what I can see, the task plugin doesn’t allow you to specify conditions for anything else but tasks, so is there any way of conditionally hiding those headers?

I suspect it has to be done via CSS, but I’m not sure that is possible since there may not be a class for empty queries. And even if there were, it would help me hide the query, but not the heading, would it?

I’ve not worked a whole lot with the tasks plugin, but I think you’re correct that it’s hard to hide the heading based on the resulting query. I’m not sure (read: has no idea) whether tasks has some API you can call, and then check the result before outputting the result. Haven’t heard about it, but that doesn’t mean it doesn’t exist.

Using dataviewjs you could opt for something like:

```dataviewjs
const tasks = await dv.query(`
TASK
WHERE !task.completed
`)

if ( tasks.result == "Successful" ) {
  if ( tasks.value.values.length > 0 ) {
    dv.header(3, "Not completed")
    dv.taskList(tasks.value.values, false)
  }
} else
  dv.paragraph("~~~~\n" + tasks.error + "\n~~~~")
```

You would still need a little translation of the various cases, and possibly some regexreplace to hide various bits and parts.

On second thought, there might be a way to handle this. One could set up a CSS rule for the header in front of the tasks blocks to be hidden if there are no <li> tags in the next div section.

Following this untested, but it kind of shows the idea:

div:has(h3) + div:has(div.block-language-tasks) {
 display: none;
}

div:has(h3) + div:has(div.block-language-tasks li) {
 display: block;
}

Untested, and most likely wrong, but something like that could possibly work. Sadly, I don’t have time tonight to test this any further…

So I got around to testing this, and didn’t get what I wanted out of it. I made a document shown in the collapsed section below, with the CSS following that again.

My test document
- [ ] A task
- [ ] Another task

----

### Not done tasks (2)
```tasks 
not done
path includes ForumStuff/f54/f54231/f54231 tasks from dvjs.md

Done tasks (0)

done
path includes ForumStuff/f54/f54231/f54231 tasks from dvjs.md

In other words, two tasks which are not done, and zero tasks which are done (and therefore hopefully should be hidden)
div>h3 {
  color: pink !important;
}

/* No tasks div selector */
div:has(h3) + div:has(div.block-language-tasks) {
  background-color: yellow;
}

/* Has tasks div selector */
div:has(h3) + div:has(div.block-language-tasks li) {
  background-color: green;
}

Gives the output:
image

Here we see that the CSS targets the latter div, and not the first div with the heading. So if only we could choose the first of the sibling selectors. Which we sadly can’t. And trying to combine the :has() selectors, only leads to selecting on elements next to the <h3> and not next to the parent <div>.

If only the parent
had a class

After some depressing tests, I then added a class “pre-block-languages-tasks” through manual editing of the DOM elements, and applied the following CSS:

div:has(>div.block-language-tasks),             
div.pre-block-language-tasks:has(+ div>div.block-language-tasks) {
  /* background-color: teal !important; /* */
  display: none;
}

div:has(>div.block-language-tasks li),    
div.pre-block-language-tasks:has(+ div>div.block-language-tasks li) {
  /* background-color: teal !important; /* */
  display: block;
}

This produces the wanted output of:
image

Sadly, I’m not sure if (or how) we should add that class to the preceeding <div>, but if one was able to do that through javascript (or similar means), then you would be able to achieve your goal. Without it, I don’t think it can be done.

(Some time goes by)

So I found that the following would indeed be able to add the necessary class to the previous <div>, but I’m not sure how to trigger this javascript at the right time (after switching to reading view that is):

document
  .querySelectorAll('div:has(>div.block-language-tasks)')
  .forEach(f => {f.previousSibling.classList.add("pre-block-languages-tasks")})

Wow, what an immense work work you’ve done there. I can really feel how you wanted to figure that one out! I’m just like that, but I simply don’t have the time at the moment, so thank you so much for taking the question this far. My knowledge in CSS is rather fragmented and superficial (feel free to read this as: has no clue), so figuring this kind of stuff out is a double challenge since I simultaneously need to teach myself the relevant bits of CSS. (BTW: ChatGPT has proven to be rather catalytic in that respect)

I will try to build on your work once my work load is back to normal levels. My hunch is that I need to submit a feature request to the developers of the tasks plugin to add a class (please help me to specify exactly what class that would have to be), but before taking that step, there are two things I’d try from here:

  1. See what chatGPT can come up with (this will not be relevant for someone who knows CSS and can tell at a glance that there is no way of targeting what we want to target, but for me that does not apply).
  2. Check out what can be done with the available classes documented at Styling | Obsidian Tasks

I am not sure I’d want to add javascript to this. It seems overkill for a job that could be much more elegantly done either in the tasks plugin itself or in CSS. Yea, as I write this I realize: that is probably the way to go: not even ask for a class, just ask for a a layout command that allows adding (conditional) markdown and hide empty results. The only problem with that is that it’s asking for quite a lot…

I’m not sure you’re able to avoid it…

The thing is that the html structure is like the following:

<div> <!-- this is the div we'd like to have a class -->
   <h3>Your heading</h3>
</div>
<div>
  <div class="block-language-tasks">
    Your task query stuff
  </div>
</div>

The issue that CSS is not very keen on going down into one part of the DOM tree (to see the <h3>), and the go back up and down into something else. This has been partly addressed with the :has() selector, but since its hard to do this in combined selectors, we’re kind of at a stand still.

Secondly, the “untagged” </div> which we would like to give a class, and which makes a lot of styling the reading view a whole lot harder, is (to my eyes at least) a somewhat useless div element which I don’t think we’re going to be able to get any changes done to, since I believe its a part of the engine generating the html from markdown, which I assume it’s using specifically to create “sandboxes” for the various elements to live within (and not break out of).

In other words, I think javascript is needed to avoid having to traverse down into multiple DOM trees (using CSS hacks), and this has been a problem for ages throughout CSS styling (which hasn’t been fixed for a few decades). In addition, I don’t think neither the Obsidian crew, nor any of the plugin crew, are in a position where they want this to be changed.

One could possibly try to persuade the tasks plugin developer team, to add an option for a custom header, and then add a custom option to present something else (or nothing at all) if the query has no results. Another option, would be to ask the tasks plugin developer team, to add an API for calling the plugin, which would then allow for checking the returned result before building the resulting view yourself.

And in which case the advantages of the easy tasks queries are gone and I might as well use dataview… :expressionless:

That is partly true, yeah. Hence also my suggestion on using javascript, if only I knew how to trigger it on the correct time, to get the wanted class added to the aforementioned <div>, as then one could use the CSS I’ve also presented, and you could get this wanted behavior without worrying anymore. :smiley:

@holroy I used some of your snippets and modified them to figure out my own issue with hiding headers and I got what I want to working so thank you for your contribution!

After, that I took the liberty of using my solution to get your use case working.

Here’s the snippets I came up with.

To hide the h3 header, which precedes an empty task list:

div:has(> h3):not(:has(+ div>div.block-language-tasks>div>ul>li)) {
  display: none;
}

To hide the div body of an empty task list:

div:has(> div.block-language-tasks):not(:has(li)) {
  display: none;
}

Some screenshots examples:

1 Like

I’m impressed! I did try various combinations, but I had troubles with getting it right, as stated above. I never thought of doing a double :has in combination with :not.

This is going straight into my bag of nifty CSS trickery!

1 Like

Unfortunately, this doesn’t seem to work for me. Maybe this is theme dependent? I’m using the Minimal theme, but I also tried it with the default one. In neither of them the empty tasks lists and their headings disappear…

To be more specific: I created a .css file in the folder for snippets (under Settings → Appearance) and pasted your two snippets into them. Then I activated the snippet.

After testing I see that there is some specificity stuff occurring, and if you’re still on 1.1.9 there is also some issue. So if you update to 1.1.15, and change your snippets to this:

div.HyperMD-header-3:not(:has(+ div>div.block-language-tasks>div>ul>li)),              
div:has(> h3):not(:has(+ div>div.block-language-tasks>div>ul>li)) {
 display: none !important;
}

div:has(> div.block-language-tasks):not(:has(li)) {
 display: none !important;
}

Now the empty task section with its header disappears for me in both live preview and reading view. It’s hidden in at least both Minimal and default theme, and most likely most other themes, as well.

Unfortunately, I can’t confirm. Still seeing all my empty lists and headers. I’m on v1.1.15… I tried disabling all other CSS snippets, but no luck. :sob:

Strange, could you list your css file, and show us a screenshot of it enabled?

Are you in live preview or reading view? Could you show the markup around the tasks query?

Just trying to eliminate causes for it not to work.

Also note that the solutions posted only work on h3 headers.

If you’re working with h1 headers aka 1 hashmark, then you’ll need to update the snippets.

e.g

h1 header

h3 header

I know you prefer the tasks plugin, and those queries, but I recently discovered that when using a dataview TASK query, hiding the queries can be incredibly easy. It doesn’t require custom CSS, and it doesn’t require any javascript either. See the link below for a little showcase displaying a similar case to your original queries:

The css is unedited copy and paste from above:

Here are some examples of empty task queries with the above CSS enabled:

CleanShot 2023-03-05 at 19.06.26

The screenshot above is in reading view but it looks the same in preview.

CleanShot 2023-03-05 at 19.13.23

If I remember correctly, one disadvantage with using dataview for tasks was that you couldn’t interact with them like you can with task queries. I don’t remember the details, though. (Ideally, I should keep notes on these kinds of things, but in the end, I’d be taking notes about me taking notes about my note taking…)

This is strange, that you’re not seeing them hidden like we do. Which version of Obsidian and Tasks are you using? I seem to remember I had some intermittent problem in one of the Obsidian version.

But these do not show when using Obsidian v1.1.16 (with installer v1.1.9) and Tasks v1.25.0.

If you’re using those, as well, then I’m really stumped why it doesn’t work for you. Do you have any other plugins or themes which might interfere with the CSS somehow? And you do have that snippet enabled within Settings > Appearances (CSS snippets) > hide-empty-task-lists.css ?

(I’ve tested with Minimal, Prism, Things and default theme, and it hides the task in all of those)

If neither of these tricks work, are you able to navigate into the Developer Tools the Element pane, and show the corresponding markup related to an empty section?

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.