Link to home
Start Free TrialLog in
Avatar of tel2
tel2Flag for New Zealand

asked on

CSS/JS Link to child in tree view

Hi Web Dev Experts,

I've coded a pure CSS accordion/tree view like this:
    https://codepen.io/Tel7/pen/RwGVQGV
As you can "see", it uses hidden (display: none) checkboxes to open <section>s when those <section>'s heading <label>s are clicked.

I need to be able to link to any heading in the tree, in such a way that when I click the link (e.g. www.mydomain.com/treeview.htm#heading1.1), the tree opens up at that point, as if someone had clicked "Heading 1.1" on the page (which would require "Heading 1" to be clicked so "Heading 1.1" was visible/clickable).  That part doesn't work yet, and it seems to be because when visiting the page only the top level heading(s) (Heading 1 in this case) will be visible, and therefore only that heading can be linked to.  How can that be done?

Do I need some JavaScript to do this?  If so, what?

In my basic understanding, I could imagine something like this might work:
- JavaScript ticks all the parent checkboxes of the heading linked to before the page loads.
But note that the links to the headings could be internal or external to the webpage, so I'm not sure how internal links can be handled since the page has already loaded.

There could be hundreds of these headings/subheadings on a webpage, nested to a depth of about 5, and I'd like to keep all the code as simple as possible.

NOTE: Although you might be able to fix the problem in codepen.io, but I don't think you'll be able to confirm a fix via codepen.io, since inbound links to anchors on codepen pages don't seem to work.

Thanks.
tel2
Avatar of HainKurt
HainKurt
Flag of Canada image

can you change the structure, to make it simpler?
or html is fixed and not to be modified any way?

+ is jQuery solution ok?
Avatar of tel2

ASKER

Good to hear from you, HainKurt.

Yes, it can all be changed, as long as it gives the same result.  (I might accept some changes to the way it works, in addition to what I've asked for.)

BTW, I've changed a few things in the original post since you posted - sorry.

+ A jQuery solution might be OK in this case if needed.
have a look at this

https://jsfiddle.net/HainKurt/1aekz9cb/

I hardcoded hashtag, but on a normal page, it should work
and this is expanding up to 5 level...

html
  <div id=headers>MENU
    <div id=H_1>H_1
      <div id=H_1_1>H_1_1

      </div>
      <div id=H_1_2>H_1_2
        <div id=H_2_1>H_2_1

        </div>
        <div id=H_2_2>H_2_2

        </div>
        <div id=H_2_3>H_2_3

        </div>

      </div>

    </div>
    <div id=H_2>H_2
      <div id=H_2_1>H_2_1

      </div>
      <div id=H_2_2>H_2_2

      </div>

    </div>
    <div id=H_3>H_3
      <div id=H_3_1>H_3_1

      </div>
      <div id=H_3_2>H_3_2
        <div id="H_3_2_1">H_3_2_1

        </div>
        <div id=H_3_2_2>H_3_2_2

        </div>

      </div>
      <div id=H_3_3>H_3_3
        <div id=H_3_3_1>H_3_3_1

        </div>
        <div id=H_3_3_2>H_3_3_2

        </div>
      </div>
    </div>

Open in new window


js
$("div", $("#headers")).on("click", function(e) {
  var expanded = $(this).children("div:first-child").is(":visible");
  console.log(expanded);
  $("div", this).hide();
  $(this).children("div").toggle(!expanded);

  console.log(this.id);
  e.stopPropagation()
});

var hash = $(location).attr('hash');
console.log("jq : " + hash);
console.log("js : " + window.location.hash);
console.log("jsl : " + window.location);

// hardcode, in jsfiddle it is not working
// delete, comment-out this on your web server
hash = "#H_3_2_1"
var h = $(hash)

console.log($(h).text());
if (h) {
  $(h).parent().parent().parent().parent().click();
  $(h).parent().parent().parent().click();
  $(h).parent().parent().click();
  $(h).parent().click();
  $(h).click();
}

Open in new window


css
#headers div {
  cursor: pointer;
  text-decoration: underline;
}

#headers div div,
#headers div div div,
#headers div div div div,
#headers div div div div div {
  display: none;
  cursor: pointer;
  text-decoration: underline;
  padding-left:20px;
}

Open in new window

Avatar of tel2

ASKER

Thanks for all that, HK.  I wish I could (re)code that fast!

I've tried to put that into a single script, and put it here:
    http://tospeirs.net/test/demos/tree_views/ee_hainkurt1.htm
I commented out the console.log's, added a line to google's jQuery 3.2.1 (is that OK?), tidied up the div's in the <body> a bit, and added a missing </div> at the end.
Doesn't seem to be working for me.  Clicking on the headings doesn't open them.  Can you see why?
move the script to the bottom, just before </body>

or wrap it with jQuery ready function
$(function() {
  //all js code goes here
});

Open in new window

Avatar of tel2

ASKER

OK, that got it working, thanks!

But now how do I un-hard-code it so I can link to it externally like this:
    http://tospeirs.net/test/demos/tree_views/ee_hainkurt1.htm#H_1_2
and internally (i.e. from elsewhere in the webpage)?
use this link

http://tospeirs.net/test/demos/tree_views/ee_hainkurt1.htm#H_3_2_1

and comment out hardcoded value, see my demo again, I added comment there...

remove/comment-out this line

hash = "#H_3_2_1"

Open in new window

Avatar of tel2

ASKER

Sorry, I'm with you now.
Checking now.
Avatar of tel2

ASKER

Am I commenting BOTH of these lines out:
hash = "#H.3.2.1"
var h = $("#H_3_2_1")
I updated my post

it should be just

var hash = $(location).attr('hash');
var h = $(hash);

Open in new window

Avatar of tel2

ASKER

Nice.  That's working when I do this:
    http://tospeirs.net/test/demos/tree_views/ee_hainkurt1.htm#H_1_2
Now I'm going to try an internal link...
Avatar of tel2

ASKER

I've put a link inside H_1_2 pointing to H_3_2_1 and it seems to work, thank you HK!

Some questions:
Q1. Was there any good reason for using underscores instead of periods in the H_1_2 ids, etc?
Q2. I see everything in the div's are getting underlined.  Can you suggest a simple way to make the headings underlines, and the rest not, as per my original codepen?  I'm sure I'd work something out, but with your experience you may have a better idea.

Thanks again.
tel2
Avatar of tel2

ASKER

Hang on...
The internal links only take me to the destination if the destination is already open.
Any ideas how to fix that so they work even when closed?
Q1. jQuery uses "." for class, so it did not work...

Q2. need to change a lot...
right now clicks are bound to menu/div whole together...
need to separate headers and paragraphs...
need to change js code to make clicks on headers only...

then, adjust css...

dont have time for that today...
later I can check...
The internal links only take me to the destination if the destination is already open.
Any ideas how to fix that so they work even when closed? 

I could not get this...
Avatar of tel2

ASKER

What do you mean by not getting it?  Can't understand what I mean, can't reproduce it, or what?

If you go here:
    http://tospeirs.net/test/demos/tree_views/ee_hainkurt1.htm#H_1_2
then click the "Link to H_3_2_1" link.
It doesn't take you anywhere unless you can already see H_3_2_1 because you've manually already opened it up.
I guess I got it...
it is because then hash tags are the ID of elements...

instead of hashtag, we should use query string

and page should be called

http://tospeirs.net/test/demos/tree_views/ee_hainkurt1.htm#H_3_2_1
>>>
http://tospeirs.net/test/demos/tree_views/ee_hainkurt1.htm?H=H_3_2_1

does this work?
Avatar of tel2

ASKER

Both of those links work when clicked from external webpages, but how do I link to H_3_2_1 from inside the webpage, e.g. under H_1_2?

Currently I've got this:
<div id=H_1_2>
        H_1_2<br>
        Paragraph 1.2<br>
        <a href=#H_3_2_1>Link to #H_3_2_1</a><br>
        <div id=H_1_2_1>
...etc...

Open in new window

But that has the problem I described above.
hashtags work like that...
if you really need to work with hashtag in url, we need to have those elements
and when you triy to go to same page with hashtag, browser just scrolls to that item, instead of reloading...

thats why I suggested below

http://tospeirs.net/test/demos/tree_views/ee_hainkurt1.htm?H=H_3_2_1 

this is how we do normally...
or maybe this works...

just add this to see if it works

  $("a", $("#headers")).click(function () {
        var hash = this.hash;
        if (hash != "" || hash!= null)
            window.location.reload();
    });

Open in new window

Avatar of tel2

ASKER

Add it where in the jQuery block, exactly?
what it does is,
when you click a link in headers element, our menu links
browser puts hashtag to the url
then we check this, if hash is something, then we reload the url
if it is null/empty, ie no hash, then it will just do normal click...
Add it where in the jQuery block, exactly? 

anywhere... in our js block...
to the bottom for example...

User generated image
Avatar of tel2

ASKER

If I go here:
    tospeirs.net/test/demos/tree_views/ee_hainkurt1.htm#H_1_2
then click my "Link to H_3_2_1" it takes me nowhere.
If H_3_2_1 is already open, it goes there then refreshes the page.
Avatar of tel2

ASKER

Anyway, it's WELL past your bedtime HK, and I've got a meeting for 1+ hours now sorry.
Thanks for your efforts.  I might check back after my meeting, or tomorrow if you decide you need sleep.
yes I see...
it does not work...
need to check...

but if you really do not need # in url
you should use query string instead...
this is what we use all the time, not hashtag this way!

http://tospeirs.net/test/demos/tree_views/ee_hainkurt1.htm#H_3_2_1
>>>
http://tospeirs.net/test/demos/tree_views/ee_hainkurt1.htm?H=H_3_2_1 
Avatar of tel2

ASKER

Are you trying to say I should use the "?H=" URL syntax instead of the "#" syntax, even when linking from within the webpage?

If you click this you'll see it's not positioning me on the page at all:
    http://tospeirs.net/test/demos/tree_views/ee_hainkurt1.htm?H=H_3_2_1
Any ideas what's wrong?

The reason the link at the bottom of your last post works is, the actual hyperlink uses the "#" syntax, though the visual text of the link uses the "?H=" syntax.
have a look at this

https://jsfiddle.net/HainKurt/3nfyjtuz/

added paragraph for each menu
removed hashtags and now working with query url parameters
only header is underlined and clickable, not paragraphs
menu structure is a bit changed...

html
<div id=headers>MENU

    <div class=menu id=H_1><span class="header">H_1</span>
      <p>
        paragraph for header 1...
      </p>

      <div class=menu id=H_1_1>
        <span class="header">H_1_1</span>
        <p>
          paragraph for header 1 1...
        </p>
      </div>

      <div class=menu id=H_1_2><span class="header">H_1_2</span>
        <p>
          paragraph for header 1 1 2...
        </p>
        <div class=menu id=H_2_1><span class="header">H_2_1</span>
          <p>
            paragraph for header 2 1...
          </p>
        </div>
        <div class=menu id=H_2_2><span class="header">H_2_2</span>
          <p>
            paragraph for header 2 2...<br>
            link to <a href="?H=H_3_2_1">H_3_2_1</a>
          </p>
        </div>
        <div class=menu id=H_2_3><span class="header">H_2_3</span>
          <p>
            paragraph for header 2 3...
          </p>
        </div>
      </div>
    </div>

    <div class=menu id=H_2><span class="header">H_2</span>
      <p>
        paragraph for header 2...
      </p>

      <div class=menu id=H_2_1><span class="header">H_2_1</span>
        <p>
          paragraph for header 2 1...
        </p>
      </div>

      <div class=menu id=H_2_2><span class="header">H_2_2</span>
        <p>
          paragraph for header 2 2...
        </p>
      </div>
    </div>

    <div class=menu id=H_3><span class="header">H_3</span>
      <p>
        paragraph for header 3...
      </p>

      <div class=menu id=H_3_1><span class="header">H_3_1</span>
        <p>
          paragraph for header 3 1...
        </p>
      </div>

      <div class=menu id=H_3_2><span class="header">H_3_2</span>
        <p>
          paragraph for header 3 2...
        </p>
        <div class=menu id="H_3_2_1"><span class="header">H_3_2_1</span>
          <p>
            paragraph for header 3 2 1...
          </p>
        </div>
        <div class=menu id=H_3_2_2><span class="header">H_3_2_2</span>
          <p>
            paragraph for header 3 2 2...
          </p>
        </div>
      </div>

      <div class=menu id=H_3_3><span class="header">H_3_3</span>
        <p>
          paragraph for header 3 3...
        </p>
        <div class=menu id=H_3_3_1><span class="header">H_3_3_1</span>
          <p>
            paragraph for header 3 3 1...
          </p>
        </div>
        <div class=menu id=H_3_3_2><span class="header">H_3_3_2</span>
          <p>
            paragraph for header 3 3 2...
          </p>
        </div>
      </div>
    </div>

  </div>

Open in new window

js
$("#headers .header").on("click", function(e) {
  var expanded = $(this).parent().find(".menu").eq(0).is(":visible");
  console.log($(this).next(".menu"));
  console.log(expanded);
  $(this).nextAll(".menu").hide();
  $(this).nextAll(".menu").toggle(!expanded);

  console.log(this.id);
  e.stopPropagation()
});

var getUrlParameter = function getUrlParameter(sParam) {
  var sPageURL = window.location.search.substring(1),
    sURLVariables = sPageURL.split('&'),
    sParameterName,
    i;

  for (i = 0; i < sURLVariables.length; i++) {
    sParameterName = sURLVariables[i].split('=');

    if (sParameterName[0] === sParam) {
      return sParameterName[1] === undefined ? true : decodeURIComponent(sParameterName[1]);
    }
  }
};

//show top levels
$("#headers").children(".menu").show();

var H = getUrlParameter("H");

// hardcode, in jsfiddle it is not working
// delete, comment-out this on your web server
H = "H_3_2_1"
var menu = $("#" + H)
console.log($(menu).text());

if (menu) expandAll(getMenuHeader(menu));

function expandAll(m) {
  m.find(".header").eq(0).click();
  var p = getParentMenu(m);
  if (p) expandAll(p);
}

function getParentMenu(m) {
  var pm = m.parent();
  if (pm.hasClass("menu")) return pm;
}

function getMenuHeader(m) {
  var h = m.find(".header").eq(0);
  return h;
}

Open in new window

css
#headers .header {
  cursor: pointer;
  text-decoration: underline;
}

#headers p {
  padding: 0px;
  margin: 0px;
}

#headers .menu {
  display: none;
  padding-left: 20px;
}

#headers {
  padding: 10px;
  border: 1px dotted gray;
  margin: 10px;
}

Open in new window

demo
https://jsfiddle.net/HainKurt/3nfyjtuz/

* comment-out/remove lIne 34 in JS, to make it work with query string "?H=H_3_2_1"
H = "H_3_2_1"

Open in new window


Avatar of tel2

ASKER

Thanks again HK.
I'll try to check it out soon.
Avatar of tel2

ASKER

Hi HK,
Thanks for all that work.
That solves some problems and creates others.  Here's my demo:
    http://tospeirs.net/test/demos/tree_views/ee_hainkurt2.htm?H=H_1_2
Let me know if I've missed something in that demo.

Current problems are:
#1. None of the contents of any headers should show when it is closed.  That includes paragraphs and anything else (see how my original codepen works).  Paragraphs inside visible headers are currently showing even if those headers aren't open.
#2. When I click the above link, it opens H_1_2, but doesn't scroll down to it - it goes back to the top.
#3. When a header is open, all it's direct children should be indented.  Currently the paragraph is not indented.  See my original codepen.
#4. When I click on the "link to H_3_2_1" under H_2_2, it opens H_3_2_1 (good), but closes H_2_2 (not as per my original, but not necessarily a show-stopper - I might be willing to go with it).

Thoughts?
Avatar of tel2

ASKER

P.S. I've added a 4th issue, above - #3.

Can JavaScript do this kind of thing?:
- Change the function of clicking a "#" link so it opens the parents of that destination header before going there?
Avatar of tel2

ASKER

Update: After consideration, I think issue #4 should probably be a show-stopper.

ID should be unique

User generated image
Avatar of tel2

ASKER

Good spotting, HK.
Which of my 4 problems will that solve?
4. When I click on the "link to H_3_2_1" under H_2_2, it opens H_3_2_1 (good), but closes H_2_2 (not as per my original, but not necessarily a show-stopper - I might be willing to go with it).
the links are opening a new page!
and passing one header in url
and it opens that one in the tree up to the root...
Avatar of tel2

ASKER

I know it does, but it closes the originally open branch where we clicked the link.
Sorry my original test data didn't contain a 2nd branch so you'd see that behaviour.  I've added Heading 2 to it now so you can open Heading 2, then click into Heading 1, etc, and Heading 2 stays open.

If you're going to provide modified HTML with unique codes, you might like to sort out the numbering as well, e.g. H_1_2 should contain H_1_2_1, etc, not H_2_1, etc.


I am lost...
there are 2 things...

1. clicking on menu items, just opens/closes or show/hides menu items
2. clicking on a link > refreshes the page, passes H in url. and when page opens, gets that value, and expands to that menu, all menu items are closed...

so, what is wrong in this logic?
Avatar of tel2

ASKER


1. clicking on menu items, just opens/closes or show/hides menu items
Yes, but if you should be able to have more than header 1 branch open at a time.  See my updated codepen and my last post.
2. clicking on a link > refreshes the page, passes H in url. and when page opens, gets that value, and expands to that menu, all menu items are closed...
If clicking a link from outside the webpage, then that's fine. because no headers were already open.  If clicking from within it, I'd rather it doesn't refresh the page, but it can if really needed.  Either way, yes it should expand that heading, but I don't want all other open headings closed.  And it needs to scroll down to the newly opened item, which is not happening with the current code I have.

Sorry if this is confusing and some of it wasn't made clear from the beginning.  I was originally expecting the solution would be an adjustment to my original codepen, and that would probably have retained the currently working features by default.
not sure if I can follow any more, but check this...

https://jsfiddle.net/HainKurt/Lukmghp5/
I added a js function, openMenu, that can be used on any link
which is used on page load as well, to open the H passed in the URL
User generated image
and if you open multiple headers, the rest is not closing...

<p>Open menu <a href="javascript:openMenu('H_1_2_2')">H_1_2_2</a>...</p>

Open in new window

Avatar of tel2

ASKER

Thanks HK.
I've put that here:
    tospeirs.net/test/demos/tree_views/ee_hainkurt3.htm?H=H_1_2_2
Look correct?

Which of the 4 problems I listed here does that solve?  Just #4?
I've put that here
I dont see it...

the link goes to
http://tospeirs.net/test/demos/tree_views/ee_hainkurt2.htm?H=H_3_2_1

but you should run javascript

<a href="?H=H_3_2_1">H_3_2_1</a>
>>>
<a href="javascript:openMenu('H_1_2_2')">H_1_2_2</a> 

and it misses that function

see my demo again
https://jsfiddle.net/HainKurt/Lukmghp5/

I added 2 links, one opens menu, the other refreshes the page/goes to that url...
2 different methods...

second one, does not keep menu structure but just expands the one passed in url...
first one, just opens menu on current page
User generated image

Avatar of tel2

ASKER

Sorry, here it is:
  tospeirs.net/test/demos/tree_views/ee_hainkurt3.htm#H_1_2

But I'll try to respond to your last post tomorrow, sorry.

I see it works

but link should be

http://tospeirs.net/test/demos/tree_views/ee_hainkurt3.htm?H=H_1_2

no more hashtags!
Avatar of tel2

ASKER

Yes, of course.  Sorry.

I haven't done much testing yet, and I have to go, but...
Problem #2 from this post seems to still be a problem.
And here's another:
#5. I don't want the "MENU" at the top, nor do I want any special padding/margins/border around the headers.  I assume the latter can be partially  achieved by removing the "#header" block from the CSS, right?  The top level headers should have no indent, just like the "Test" rows.
Avatar of tel2

ASKER

OK HK, I've had another look at your latest code and put it here:
    http://tospeirs.net/test/demos/tree_views/ee_hainkurt4.htm?H_1_2_2
Are you able to address the 2 outstanding issues I mentioned in my last post?
if you want to scroll to any element use

document.getElementById(H).scrollIntoView();

Open in new window

after opening the menu...
ASKER CERTIFIED SOLUTION
Avatar of HainKurt
HainKurt
Flag of Canada image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of tel2

ASKER

Thanks HK,
OK, I've removed the word "MENU" from the HTML, and replaced all the CSS with your latest and put it here:
    https://jsfiddle.net/tel777/w2xsuvej/
The layout now looks OK I think, but I might look at your:
    document.getElementById(H).scrollIntoView();
suggestion for next year when I have time.
Meanwhile, I think I'll leave this question open.
Thanks again for your help so far HK.
dont leave open...
whenever you have a question, open a new one for each question...
Avatar of tel2

ASKER

I'm not planning to ask completely new questions in this thread, HK.  Only sub-questions to get this thing working in the way I have asked it to work.  At this stage, I haven't tested your suggestion re scrollIntoView(), partly because I haven't made time to work out where to put it in your code.  That's why I intend to leave it open for now.  Any problems with that plan?
Avatar of tel2

ASKER

Hi HK,
Sorry for the long delay in responding.
Thanks for your efforts.
Not exactly what I was after, especially the part where even clicking links inside the webpage reload the page, but I learned some things thanks to you, and I'll award points and close this for now.