Adding a changing icon / symbol (+/-) expanding/collapsing a text

I use a jquery simple script to expand/collapse short text:

function expand(divId) {
	$("#" + divId).toggle();	
}

And in html:

<a href="javascript:expand('name');" class="internal">visible text</a>
<span class="hide" id="nome">invisible, expansible text</span> 

And that code works perfectly.
But I wish add a changing icon ("+" when collapsed, “-” when expanded) after the expansible link, modifying the less possible the jquery code and the html code.
Any idea?
Thanks!

use .text() or .html() to set the link’s text.

quick and dirty

function expand(divId) {
     // just so only one DOM search has to be done....
    var div = $("#" + divId);
    // toggle visibility
    div.toggle();
    // show appropriate +/- depending on visibility of div.
    $(".internal").html(div.is(":visible") ? "-" : "+");
}
2 Likes

I would do it like this:

<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="utf-8">
  <title>Document</title>
  <style>
    .hide { display: none; }
    span { color: blue; text-decoration: underline; cursor:  pointer }
  </style>
</head>

<body>
  <span class="expandable">Read more +</span>
  <div class="hide" id="nome">Lorem ipsum dolor sit amet</div>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
  <script>
    $('.expandable').on('click', function(e) {
      e.preventDefault();
      $(this).toggleClass('expanded');
      $(this).next().toggleClass('hide');
      const isExpanded = $(this).hasClass('expanded');
      $(this).text(isExpanded ? 'Read less -' : 'Read more +');
    });
  </script>
</body>
</html>

Edit: I made the anchor tag into a span element, as if the link has the sole purpose of running some JavaScript code it doesn’t qualify as a link.

4 Likes

Thank you. I prefer the code of @DaveMaxwell : the simpler, the better.
But there are some further improvements. I forgot to say that so far I used already an icon, but a static one, with this css:

a[href^="javascript"]:after {
  content: '\002B'
}

Moreover the code of @Dave replace (after clicking, with a “+”) the text I can put between the <a>...</a>, and this is a problem.

Then a combination of my approach and James’ could work (his toggleClass approach is more elegant and controllable).

You’d need to change your css a bit…

.hidden:after {
  content: '\002B';  /* + icon */
}
.visible:after {
  content: '\002D'  /* hyphen icon*/
}

then add a class to your html

<a href="javascript:expand('nome');" class="internal hidden">visible text</a>
<span class="hide" id="nome">invisible, expansible text</span> 
function expand(divId) {
    // show/hide text
    $("#" + divId).toggleClass('hide');
    //  change icon
    $(".internal").toggleClass('hidden').toggleClass('visible');
}
1 Like

Unfortunately this new code is worse than the first: does’nt work as expected. Besides, clicking an element changes all the elements with that class.

I guess that css should be:

a.internal:after {
  content: ' \002B';
  font-weight: bold;
  margin-left: 5px;  
}
a.minus:after {
  content: ' \002D';
  font-weight: bold;
  margin-left: 5px;  
}

and js something like

function espandi(divId) {
	$("#" + divId).toggle();
	$(".internal").html(div.is(":minus") ? "-" : "+");
}

to alternate minus:after (replacing internal:after) so that after be the correct icon.

EDIT
I mean it seems to me, that the better solution would be to change css class to <a> element between an :after and another one.

I would use the code supplied by James and just adjust the icon with a css class.

Off-topic: As James said that anchor should really be another element like a span but more semantically a button as it is an action not a link.

3 Likes

Uhm… but in my website there are several IDs to working with, not only one (#name), as in your (working) example.
Therefore your code, as is, doesn’t work in my website (and, I guess, in any website with several IDs to expand) . Maybe with some fix… I guess, using a class, rather than an ID.

Between <a> and </a> there is a text, semantically consistent, not deceiving.

a are anchors for links to go somewhere. It could be to another portion of the same page, or somewhere entirely different. But it implies a destination.

That’s not what you’re doing here. What you’re doing is more consistent with a button more so than a link, if anything. But even a button implies an actionable form is going to operate.

Actually, a checkbox is probably more semantically correct than anything because you’re toggling a value. You can use the value and some JS to dictate what element to show/hide

<input type="checkbox" id="expandable" class="expandable" value="nome" />
<label for="expandable">visible text</label>
<span class="hide" id="nome">invisible, expansible text</span> 

You can use css to change the +/-. No javascript needed

.expandable { display: none;}
.expandable+label:after {
    content: ' \002B';
  font-weight: bold;
  margin-left: 5px; 
}
.expandable:checked+label:after {
    content: ' \002D';
    font-weight: bold;
    margin-left: 5px; 
}

JS to show/hide is simple and clean…

$('.expandable').on('click', function(e) {
    const chkValue = $(this).val();
    $('#' + chkValue).toggleClass('hide');
});
1 Like

It works with multiple elements and no change to the js needed at all.

1 Like

OK, this time it works, but I have a problem with the style of the expanded text.
I wish it be
{
display: block;
color: navy;
font-size: 85%;
}
Instead, now is a “normal” (as color and size) style, displayed as inline.
How should I do?

Thank you.
Maybe you are right, but the change I should do is too big (I have hundred of this type of expansible link).
Besides an <input> tag seems to me even less semantic :thinking:

I would just add a new class by default in the html and use that for the styling. Here I have added a class called expand by default.

e.g.

<span class="expand hide" etc...

Is that what you meant? Are you ok to add classes or are there restrictions on the html etc?

1 Like

I have to do some new attempts, but it seems quite correct. Thank you!

The main problem is that the display:block. If setted the invisible text becomes always visible (expanded).
Therefore, so far, I have to accept an inline display. I’d like something like display:block …

It’s display block in my last example but the order of the css is very important.

The display:block is first in the css.

.expand {
  display: block;
  color: navy;
}
.hide {
  display: none;
}
<a href="#" class="internal expandable">visible text</a>
<span class="expand hide" id="name2">invisible, expansible text</span>

The hide class gets removed by the js and then the element takes on the display:block from the .expand style.

1 Like

Thank you. It seems ok!
I had thought another solution:

.hide {
  visibility: hidden; 
  max-height: 0;
}

.expand {
  padding-left: 2%;
  color: navy;
  font-size: 85%;
  display: block;
}

But yours is absolutely better!
Thank you very much!!

1 Like