Anchor onclick -- changing innerHTML of enclosing div?

I found a function online that allows me to swap text onclick and have amended it as follows:

function swapText(obj, n) {
    if(obj.oldText){
        obj.innerHTML = obj.oldText;
        obj.oldText = null;
    } else {
        obj.oldText = obj.innerHTML;
        obj.innerHTML = '<h1>'+n+'</h1>';
    }
}

…and use it like this…

<a id="lnk1121" href="#" onclick="swapText(this, 1);">1</a>

…obviously, this replaces the text of the clicked link with an h1-styled number. Clicking it repeatedly toggles it back and forth fine as it (and every other link) has a unique id. I have groups of numbers hyperlinked like this, each group encased in a div, also with a unique id.

What I’d like to achieve is this:

…when any link in a given group is clicked, the content of the enclosing div is swapped out with the h1 representation of the number that was clicked – and then when that h1 number is clicked, it shrinks back down to size and appears as it did before with the other link(s) in that div.

I have this partially achieved… when clicked, any number will indeed “turn into” an h1’d representation of itself…but I can’t get the other links in the group to disappear when the h1’d number appears (onclick).

Can anyone help?

Thanks!

Cranjled

Is it necessary to wrap the content in a new tag? You could simply apply a new class to the element to change its style, and inside the function search for other elements with that class and remove it. This isn’t TOO hard to do in modern browsers with getElementsByClassName support but failing that you can use this: http://robertnyman.com/2008/05/27/the-ultimate-getelementsbyclassname-anno-2008/ to add it to the browsers.
If you’re feeling adventurous you may wish to take a look at jQuery, a magnificent javascript library that makes tasks like this absolutely trivial to perform.

All content is already wrapped in tags. I avoided doing a class change (ie, display:none) to hide things as this causes the positioning of everything else to move left (due to floating in divs). I have posted the whole code (js+html) so you can see what I’m trying to achieve in first person. Any suggestions you have would be appreciated.

I’m not a JS programmer by any means (PHP is my thing) and kind of muddling my way through (that page took me 2 solid days to get it to that point). There are a couple of things I still want to achieve with it, but would be ecstatic just to be able to get my h1’d numbers to un-h1 when they’re right-clicked, then I’ll deal with more features… :wink:

Sudoku Helper

Thanks so much for your reply.

Cranjled

Right, I see what you’re doing there. It doesn’t have to be a class CHANGE, a class addition would suffice.

From looking at your code here, without much modification it’s not to difficult to see a viable option: Don’t replace the cells’ contents.

What you could do is set up another element in each square, and chance its contents to match that of the anchor that was clicked. Then when that link is clicked, you can empty it out and the other numbers will be shown again without having to recreate all their table cells and onclicks.

I really would recommend learning how to use jQuery for this though, it would make this project very easy, and make it simple to hide nearby numbers that have been clicked (Like the 7’s in your example.)

Assuming you’d rather avoid that, I’ve found the main issue with your javascript.

The function you’re calling to swap your text isn’t remembering anything because obj is defined inside the function. Furthermore, the showHideNumberContainer function stops firing because tot he H1, the call is only providing 2 parameters instead of 4. I’ve rewritten it to work properly. Try this out:

<!-- SHOW/HIDE NUMBER BOX (around 9 tiny numbers) AND REPLACE/UNREPLACE WITH BIG H1 NUMBER -->
boardMemory = new Object

function showHideNumberContainer(b, r, c, p)
{
	var sid = String(b)+String(r)+String(c);
	var lid = String(b)+String(r)+String(c)+String(p);
    var obj = document.getElementById('numberContainer'+sid);

    if(boardMemory[sid]){
        obj.innerHTML = boardMemory[sid];
        boardMemory[sid] = null;
    } else {
        boardMemory[sid] = obj.innerHTML;
        obj.innerHTML = '<a id="lnk'+lid+'" href="#" oncontextmenu="showHideNumberContainer('+String(b)+','+String(r)+','+String(c)+','+p+');"><h1>'+p+'</h1></a>';
    }
}

Hey, that was fantastic, thanks for both the amended code AND explanation. It does indeed toggle back and forth – which led me to wonder: would it remember the state of any previously hidden possibilities in the given cell after being h1’d and back…and it does! :slight_smile: Again, thanks…

I did look at jquery at your suggestion. It could really rock this out!! …if I knew what I was doing with it. :frowning: I’m afraid using it would add more of a learning curve still. I’m not a JS guy and while the demos/examples of jquery are awesome, I really have no idea how to even start to put my grid-script together in it, unfortunately.

I suppose the only missing feature in the grid would be the automatic removal of related digits (from row, col, blk…when a number was selected), but… well, that’s beyond me!

Thanks again for your awesome help!

:slight_smile:

Hi,

Well, with surprisingly little(r) JS than I was expecting, I have gotten this thing almost whooped – and with the extra features!!!

…but I wonder if you might take one more look at something that’s not quite right.

I have gotten the script to remove the related numbers when you set a cell – and to replace them when you de-set the cell, but there are 2 problems.

  1. The state of hidden cells is not being remembered when I run the showHideRelated() function. I see why, but don’t understand the solution.

  2. Set any cell as any number. You cannot set any other cells that are in the row, block, or column as an already-set cell (already-set, meaning the H1’d version of the number is showing in that cell instead of the tiny number panel.) The previously-set cell(s) seems to provide a stumbling block. Notably, if you have a number set in a row/column/block and try to set another number for that row/column/block, the showHideRelated() still works on the row/column/block – so long as they don’t also have a stumbling block in the way. Conversely, this happens all the way around.

Wow, I sure hope that made sense…

With your previous help – and some trial and error – and a bit of reading – I’ve got it almost worked out. The JS code could likely be tightened up, but not a huge concern since the entire file – PHP, HTML, JS and CSS is only 11kb including tons of comments and spacing. :slight_smile:

I know you probably have a ton of better things to do, but if you could take a look at this again, it would be extremely appreciated. The link is below.

Thank you,

Cranjled

Sudoku Solution Helper

I have a few of the problems solved.

First, when there is a large number on the board you cannot make a second.

The problem is here

       //----------------------------------------
    // Show or hide candidates in same row.
    //----------------------------------------
    // An iteration for each cell in row.
    for (i=1; i<=9; i++) {
        // Only show/hide (tiny number) if not in the clicked cell.
        if (i != c) {
            // Assemble id of number to show/hide.
            var id = 'lnk'+String(b)+String(r)+i+String(p);
            // Check if number should be shown or hidden.

				if (action == 'show') {
					// Show number.
					document.getElementById(id);.innerHTML = p;
				} else {
					// Hide number.
					document.getElementById(id);.innerHTML = '&nbsp;';
				}
        }
    }

Once a number has been selected, the next one is going to try to act on an ID that no longer exists. To fix this, store the element in a variable and test that the element exists before acting on it.

    //----------------------------------------
    // Show or hide candidates in same row.
    //----------------------------------------
    // An iteration for each cell in row.
    for (i=1; i<=9; i++) {
        // Only show/hide (tiny number) if not in the clicked cell.
        if (i != c) {
            // Assemble id of number to show/hide.
            var id = 'lnk'+String(b)+String(r)+i+String(p);
		var element = document.getElementById(id);
            //Check if the element exists.
			if(element){
				// Check if number should be shown or hidden.
				if (action == 'show') {
					// Show number.
					element.innerHTML = p;
				} else {
					// Hide number.
					element.innerHTML = '&nbsp;';
				}
			}
        }
    }

This has to be done in similar fashion for columns as well.

Next, you’re able to click numbers that have been hidden.
This is because there’s nothing checking whether they’ve been hidden or not.

I suggest making a new function to be called for onclick that will pass values to the other functions after a simple test.

function puzzleAction(b, r, c, p, action)
{
	var lid = String(b)+String(r)+String(c)+String(p);
	var text = document.getElementById("lnk"+lid).innerHTML;
	
	if (text !== '&nbsp;'){
		showHideRelated(b, r, c, p, action);
		showHideNumberContainer(b, r, c, p);
	}
}
<div class="number" id="div1111"><a onclick="puzzleAction(1,1,1,1,'hide');" oncontextmenu="showHideNumber('lnk1111',1);" href="#" id="lnk1111">1</a></div>

I’ve also been working on this…with some trial and error… added a variable to hold cell memory modeling after what you’d demonstrated. And now, incorporating your suggestions, there’s only one issue left – and it’s rather small at that!

Say you click a 5 in any row…that sets the cell and the other related 5s disappear. Now, click say a connected 4 ( or any number connected by row, column or block to the 5)… ok, good … both numbers are set.

Now unclick the 5…all good still…

Now unclick the 4… problem! when you unclick the 4, you’ll find the 5 is now missing from that cell since that 5 was hidden from the unclicking of the other 5, if you will. I’ve kept up the comments and reposted the code, if I might be so bold. :slight_smile:

Thanks so much for your help thus far and hoping you can spot this last issue.

Cranjled

Sudoku Solution Helper

Right. This last issue is the toughest. There are a couple ways to try to tackle it:
First: Try to edit the html currently stored in boardMemory,
Second: Scrap boardMemory in lieu of another method of displaying the selected numbers.

I would really recommend the second method, as we can really eliminate the need to replace the entire cell’s contents.

Here’s what I suggest:

Currently, your cells look like this:


<div id="numberContainer111">
<div class="number" id="div1111"><a onclick="puzzleAction(1,1,1,1,'hide');" oncontextmenu="showHideNumber('lnk1111',1);" href="#" id="lnk1111">1</a></div>
<div class="number" id="div1112"><a onclick="puzzleAction(1,1,1,2,'hide');" oncontextmenu="showHideNumber('lnk1112',2);" href="#" id="lnk1112">2</a></div>
<div class="number" id="div1113"><a onclick="puzzleAction(1,1,1,3,'hide');" oncontextmenu="showHideNumber('lnk1113',3);" href="#" id="lnk1113">3</a></div>
<div class="number" id="div1114"><a onclick="puzzleAction(1,1,1,4,'hide');" oncontextmenu="showHideNumber('lnk1114',4);" href="#" id="lnk1114">4</a></div>
<div class="number" id="div1115"><a onclick="puzzleAction(1,1,1,5,'hide');" oncontextmenu="showHideNumber('lnk1115',5);" href="#" id="lnk1115">5</a></div>
<div class="number" id="div1116"><a onclick="puzzleAction(1,1,1,6,'hide');" oncontextmenu="showHideNumber('lnk1116',6);" href="#" id="lnk1116">6</a></div>
<div class="number" id="div1117"><a onclick="puzzleAction(1,1,1,7,'hide');" oncontextmenu="showHideNumber('lnk1117',7);" href="#" id="lnk1117">7</a></div>
<div class="number" id="div1118"><a onclick="puzzleAction(1,1,1,8,'hide');" oncontextmenu="showHideNumber('lnk1118',8);" href="#" id="lnk1118">8</a></div>
<div class="number" id="div1119"><a onclick="puzzleAction(1,1,1,9,'hide');" oncontextmenu="showHideNumber('lnk1119',9);" href="#" id="lnk1119">9</a></div>
</div>

What you could do is modify them to look like this:

<div id="numberContainer111">
<div class="number" id="div1111"><a onclick="puzzleAction(1,1,1,1,'hide');" oncontextmenu="showHideNumber('lnk1111',1);" href="#" id="lnk1111">1</a></div>
<div class="number" id="div1112"><a onclick="puzzleAction(1,1,1,2,'hide');" oncontextmenu="showHideNumber('lnk1112',2);" href="#" id="lnk1112">2</a></div>
<div class="number" id="div1113"><a onclick="puzzleAction(1,1,1,3,'hide');" oncontextmenu="showHideNumber('lnk1113',3);" href="#" id="lnk1113">3</a></div>
<div class="number" id="div1114"><a onclick="puzzleAction(1,1,1,4,'hide');" oncontextmenu="showHideNumber('lnk1114',4);" href="#" id="lnk1114">4</a></div>
<div class="number" id="div1115"><a onclick="puzzleAction(1,1,1,5,'hide');" oncontextmenu="showHideNumber('lnk1115',5);" href="#" id="lnk1115">5</a></div>
<div class="number" id="div1116"><a onclick="puzzleAction(1,1,1,6,'hide');" oncontextmenu="showHideNumber('lnk1116',6);" href="#" id="lnk1116">6</a></div>
<div class="number" id="div1117"><a onclick="puzzleAction(1,1,1,7,'hide');" oncontextmenu="showHideNumber('lnk1117',7);" href="#" id="lnk1117">7</a></div>
<div class="number" id="div1118"><a onclick="puzzleAction(1,1,1,8,'hide');" oncontextmenu="showHideNumber('lnk1118',8);" href="#" id="lnk1118">8</a></div>
<div class="number" id="div1119"><a onclick="puzzleAction(1,1,1,9,'hide');" oncontextmenu="showHideNumber('lnk1119',9);" href="#" id="lnk1119">9</a></div>
<div class="selectedNumber"></div>
</div>

When a number is clicked, get the anchor’s number and fill the selectedNumber with the same contents you’re currently injecting into the cell.

Make selectedNumber take 100% height and width of its cell, and give it a z-index of 2, and a white background.

That way it will cover up the numbers in its cell, but they will still be there and the javascript will still be able to act upon them when the selectedNumber is deselected.

I’ve gotten my solution to work. You can test it at http://dev.techrats.net/Tests/sudoku.html, and if you would like to use it I’ll let you know what changes need to be made and why.

I’ve also caught another bug, this time showing numbers that aren’t valid. Click 1111, then 1241, then 1241 again. 1211 and 1141 are now incorrectly being displayed.

Yes, thanks for catching that other bug as well. Your version appears flawless! I would very much like to understand the ‘why’ behind this. I think I’ve beat to death that I’m not a JS guy, but I DO love to learn things! :slight_smile: I could just copy and paste what’s working on your site…but that doesn’t learn me anything that might save me from asking questions later. :slight_smile: Thanks again for your continued assistance with this.

I discovered a behavior…

Set two 4s so that their path-of-relateds (ie, those hidden) intersect. Now, unset one of the 4s and the related 4s are re-displayed…except that one of them shouldn’t have redisplayed as it conflicts with the still-set 4.

Alright, first let’s deal with the CSS:



td > div{position:relative;}

div.selectedNumber{
	position:absolute;
	height:40px;
	width: 40px;
	background:#FFF;
	line-height: 40px;
	display: none;
}


These will keep things displaying as they are, but some browsers won’t like
td > div, so your numberVontainers should be given a class that they can all be referenced by at once.

Now the javascript:

// LEFT-CLICK: Toggle between (tiny 1-9) number panel or (H1) selection.
function showHideNumberContainer(b, r, c, p)
{
    // Cast args for cleaner code.  The are
    // used as strings throughout this function.
    var b = String(b);
	var r = String(r);
	var c = String(c);
	var p = String(p);
    // Assemble short id: band+row+column
    var sid = b+r+c;
    // Assemble long id: band+row+column+possibility
    var lid = b+r+c+p;
    // Get affected number container (div).
    var obj = document.getElementById('numberContainer'+sid);
	selectedNumberDiv = obj.childNodes.item(obj.childNodes.length-2);
    // Check if board memory contained short id.
    if(boardMemory[sid]){
        // If yes sid: change div's HTML to what was in memory and then reset HTML in memory.
        //cellsMemory[b+r+c] = false;
        //obj.innerHTML = boardMemory[sid];
        boardMemory[sid] = null;
		selectedNumberDiv.innerHTML = "";
		selectedNumberDiv.style.display = "none";
    } else {
        // If no sid: create sid in memory from default HTML of div and then set div's HTML to H1 anchor.
        //cellsMemory[b+r+c] = true;
        boardMemory[sid] = obj.innerHTML;
        selectedNumberDiv.innerHTML = '<a id="lnk'+lid+'" href="#" onclick="puzzleAction('+b+','+r+','+c+','+p+',\\'show\\');" oncontextmenu="puzzleAction('+b+','+r+','+c+','+p+',\\'show\\');"><h1>'+p+'</h1></a>';
		selectedNumberDiv.style.display = "block";
    }
}

That’s my code as it stands.


// Get affected number container (div).
    var obj = document.getElementById('numberContainer'+sid);
	//This looks inside the numberContainer, and grabs the second to last element using the objects ChildNodes. childNodes is zero based and the last element in it is a text node, so the second to last is the selectedNumber div.
	selectedNumberDiv = obj.childNodes.item(obj.childNodes.length-2);


        //This can actually be optimized a little more. We don't need board memory at all
if(selectedNumberDiv.style.display === "block"){
        // If Selected div is showing, hide it and empty it.
	selectedNumberDiv.innerHTML = "";
	selectedNumberDiv.style.display = "none";
    }
else {
        // If selected div is hiding, populate it with the H1 and anchor, and display it.
        selectedNumberDiv.innerHTML = '<a id="lnk'+lid+'" href="#" onclick="puzzleAction('+b+','+r+','+c+','+p+',\\'show\\');" oncontextmenu="puzzleAction('+b+','+r+','+c+','+p+',\\'show\\');"><h1>'+p+'</h1></a>';
		selectedNumberDiv.style.display = "block";
    }

And of course, the HTML

<div class='selectedNumber'></div>

To be added at teh end of all content containers.

Thanks, that makes good sense. I’m cleaning out the needless lines and commenting your additions.

As for the related paths bug, you’ll need a function that can check is a specific number in a square is valid, and run that check any time you intend to unhide a number.

I think I introduced the “collision course” bug… I commented out the following:

//if (i==c) {
//action = 'show';
//}

…and it seems half-smooshed… :slight_smile:

Thanks again for your awesome help!

It’s really no trouble.

One thing though, if you’re referring to the if (i==c) on line 86, that block didn’t do anything.
You were checking if i==c inside of a block that runs only if i!=c.

Ah yes, you’re right… I’d commented away my i==c for some additional testing. :slight_smile: While I tend to take a great deal of pride in solving problems with as little assistance as possible, this was one of those times where, without your help, this would not likely come to fruition – namely due to time constraints (I’ve had to limit it to free time only…which is rapidly ending…so THANKS for helping me wrap this up “in time,” as it were.)

At any rate, I do plan to make this a publicly accessible tool because, well, even with that crash-course error, this thing rocks! I’ve been able to solve sudoku puzzles at 3 higher levels of difficulty – and without taking hours on them.

With the repeated help you provided, I feel like you’ve had a substantial hand in this project, and would like to include your credit information in the liner notes – if the code isn’t an embarrassment to you, of course! No doubt, some curious souls will want to know… :slight_smile:

Please let me know, either here on this thread or in a message, how you’d like your credit to appear.

Thanks so much!!

Cranjled