Hi meta, Help please!
tl;dr I’ve wracked my (limited) brain on this but with no joy. Can someone tell me how I can hide the non-chosen poll items once you’ve participated in a poll?
For example, here is a screenshot of a poll where two people have voted:
What I would like to do, once there are results to view (i.e. I’ve taken part in the poll), is to remove the entries that have not received any votes (3. and 4. in this example).
Like this.
Now, I’ll concede that I’m far from being a CSS expert, but I’ve tried everything I can think of to target the elements that represent the options that have no votes.
Here’s the HTML I’m dealing with.
<div class="cooked">
<p>Here is my poll</p>
<div data-poll-name="poll2" data-poll-type="regular" class="poll">
<div>
<div class="poll-container">
<ul class="results">
<li class="">
<div class="option">
<p>
<span class="percentage">50%</span>
<span>This one is too (but by somebody else)</span>
</p>
</div>
<div class="bar-back">
<div style="width:50%" class="bar">
</div>
</div>
<ul class="poll-voters-list">
<div class="poll-voters">
<li><a class="trigger-user-card " data-user-card="DanG"><img alt="" width="20"
height="20" src="/letter_avatar_proxy/v2/letter/d/bc79bd/20.png" title="DanG"
class="avatar"></a> </li>
</div>
</ul>
</li>
<li class="chosen">
<div class="option">
<p><span class="percentage">50%</span><span>This will be chosen</span></p>
</div>
<div class="bar-back">
<div style="width:50%" class="bar"></div>
</div>
<ul class="poll-voters-list">
<div class="poll-voters">
<li><a class="trigger-user-card " data-user-card="DanAdmin"><img alt="" width="20"
height="20" src="blah"
title="DanAdmin" class="avatar"></a> </li>
</div>
</ul>
</li>
<li class="">
<div class="option">
<p><span class="percentage">0%</span><span>And nor has this</span></p>
</div>
<div class="bar-back">
<div style="width:0%" class="bar"></div>
</div>
<ul class="poll-voters-list">
<div class="poll-voters"></div>
</ul>
</li>
<li class="">
<div class="option">
<p><span class="percentage">0%</span><span>But this one has not been selected by anyone</span></p>
</div>
<div class="bar-back">
<div style="width:0%" class="bar"></div>
</div>
<ul class="poll-voters-list">
<div class="poll-voters"></div>
</ul>
</li>
</ul>
</div>
<div class="poll-info">
<p><span class="info-number">2</span><span class="info-label">voters</span></p>
</div>
<div class="poll-buttons"><button class="widget-button btn btn toggle-results btn-icon-text" aria-label="Back to your votes"
title="Back to your votes"><svg class="fa d-icon d-icon-eye-slash svg-icon svg-node" aria-hidden="true">
<use xlink:href="#far-eye-slash"></use>
</svg><span class="d-button-label">Hide results</span></button><button class="widget-button btn btn toggle-status btn-danger btn-icon-text"
aria-label="Close the poll" title="Close the poll"><svg class="fa d-icon d-icon-lock svg-icon svg-node"
aria-hidden="true">
<use xlink:href="#lock"></use>
</svg><span class="d-button-label">Close</span></button></div>
</div>
</div>
</div>
What have I tried?
Theme component CSS
I’ve tried creating a theme component with CSS to target the elements I don’t want to display and applying display: none
.
The issue, for my very limited CSS skills, is that I want to remove the li
child elements of ul.results
only if the embedded ul.poll-voters-list
contains an :empty
div.poll-voters
.
i.e. I want to apply display: none
to the li
based on whether a grandchild div
is empty.
li -> Hide this...
ul.poll-voters-list
div.poll-voters -> ...if this has no children
So my selector to get to the empty div
is something like this:
.poll .results div.poll-voters:empty {
display: none;
}
That works fine, but I need to target the display: none
on the li
element two levels up.
See <!-- comments -->
below:
<div class="poll"...
.
.
.
<ul class="results">
<li class=""> <!-- I want to display this because the embedded div.poll-voters is not empty-->
<div class="option">...</div>
<div class="bar-back">...</div>
<ul class="poll-voters-list">
<div class="poll-voters"> <!-- NOT EMPTY-->
<li><a class="trigger-user-card " data-user-card="DanG"><img alt="" width="20"
height="20" src="blah" title="DanG"
class="avatar"></a> </li>
</div>
</ul>
</li>
<li class="chosen"> <!-- I want to display this because the embedded div.poll-voters is not empty-->
<div class="option">...</div>
<div class="bar-back">...</div>
<ul class="poll-voters-list">
<div class="poll-voters"> <!-- NOT EMPTY-->
<li><a class="trigger-user-card " data-user-card="DanAdmin"><img alt="" width="20"
height="20" src="blah"
title="DanAdmin" class="avatar"></a> </li>
</div>
</ul>
</li>
<li class=""> <!-- I want to TARGET with display: none this because it is not empty-->
<div class="option">...</div>
<div class="bar-back">...</div>
<ul class="poll-voters-list">
<div class="poll-voters"></div><!-- EMTPY! -->
</ul>
</li>
</ul>
.
.
.
</div>
I’m sure someone with CSS/ SCSS expertise would have been able to set me right on this, but I soldiered on.
I spent a couple of days Googling and learning and found some kindred spirits who each lamented the omission of a parent selector in CSS. The general consensus seemed to be that JavaScript was the way to go.
Theme Component JavaScript / JQuery
I did a bit of mucking around with JQuery many years ago and thought I’d be able to get what I wanted with this. I soon realised that I’m not dealing with a ‘typical’ web page in Discourse. Instead new posts are added to topics dynamically as we scroll.
So a simply JQuery might work as long as the poll was not ‘off screen’ when the page was first rendered, but as soon as a new poll was scrolled into view, my JQuery code wouldn’t fire.
So, I dug around meta and found and read the excellent Developer’s Guide to Discourse Themes and realised I could jump on the back of the api.decorateCooked
function to get the div.cooked
structure each time it is added to the page.
I set out to write some code to target a div
that is :empty
with a class of poll-voters
inside the div.poll
structure, jump back up two .parents()
and .addClass('bletch')
so that I could then target that class with some simple CSS…
.bletch {
display: none;
}
Well, to cut a VERY long story short, I had some fun, learned a lot but it LOOKS LIKE, the div.cooked
structure that gets sent to the api.decorateCooked
function for polls only includes the first level of the poll. Perhaps this is due to the dynamic nature of polls?
This made for a very frustrating time at the keyboard. Chrome’s dev tools were showing the structure and I’m looking at my code and can’t work out why I’m not able to target the elements that I can see in Chrome’s debugger!
To try to regain my sanity, I added this code to the </head>
section of the theme component…
$( document ).ready(function() {
e = $('.poll .results div.poll-voters:empty');
e.parent().parent().addClass('bletch');
}
and sure enough I see this…
Some questions
Is it the case that the api.decorateCooked()
function isn’t getting the entire polls structure but that it is instead fleshed out some time after the api.decorateCooked()
function is invoked?
Is there another function I can sit on, perhaps called after new posts have been added to the topic?
Is there a function that is called once the polls are ‘cooked’?
If any JavaScript whizzes can put my right on this I’d be very grateful.
Or, hopefully, more likely someone can suggest a simple CSS selector that would get what I want?
Any advice gratefully received.
B