Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations derfloh on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How to show the proper layer with Find in Page script

Status
Not open for further replies.

peterpeter2

Technical User
Joined
May 17, 2006
Messages
4
Location
NL
Hi,

I want to use a Find in Page script to let visitors search for text on a page. However, my pages are designed with Layers that are positioned at the same absolute location, all on top of each other. The script DOES find the text - even in hidden layers -, but I don't know how to show the the hidden Layer the text was found in. Can anyone please show me how to do that?

The Find in Page script code is as shown below.

Code:
var NS4 = (document.layers);    // Which browser?
var IE4 = (document.all);

var win = window;
var n   = 0;

function findInPage(str) {
  var txt, i, found;
  if (str == "")
    return false;

  // Find next occurance of the given string on the page, wrap around to the
  // start of the page if necessary.
  if (NS4) {

    // Look for match starting at the current point. If not found, rewind
    // back to the first match.
    if (!win.find(str))
      while(win.find(str, false, true))
        n++;
    else
      n++;

    // If not found in either direction, give message.
    if (n == 0)
        alert("Search text not found");
  }
  if (IE4) {

    var range = win.document.body.createTextRange();
    // Find the nth match from the top of the page.
    for (i = 0; i <= n && (found = range.findText(str)) != false; i++) {
      range.moveStart("character", 1);
      range.moveEnd("textedit");
    }

    // If found, mark it and scroll it into view.
    if (found) {
      range.moveStart("character", -1);
      range.findText(str);
      range.select();
      range.scrollIntoView();
      n++;
    }


    // Otherwise, display message.
    else {
      if (n > 0) {
        n = 0;
//        findInPage(str);
        alert("This was the last one.");
      }

      // Not found anywhere, give message.
      else
        alert("Search text not found");
    }
  }
  return false;
}

An example of my page can be found at: http://test.vandervliet-online.com/1a_omgeving.html"][/URL] The various layers (containing an image and some text describing the image) are selected by clicking an image on the left side of the screen.

Thanks in advance for any help I can get.
Peter
 
Not a solution but thought I would comment.
If the layer is hidden do you really want to allow the find command to search it?
If you do then also consider, what if the same data is found on more than one of the hidden layers? It would dramatically complicate things.

Allowing search on the hidden layers really depends on why the layer is hidden in the first place. If it is a layer just hidden to save screen space until the data is needed then it makes sense to search it. If the data is only relevant under specific on-page circumstances then it may not make sense to search it.

Sorry it is not an answer to your question but I figured these thoughts might require you to reconsider the approach and if so better that it is done now rather than after you figure the problem out and determine you have to rewrite anyway.


It's hard to think outside the box when I'm trapped in a cubicle.
 
Theniteowl, thank you for your comment. There are a few reasons I chose this solution - userfriendlyness and maintenability are two of them - but going into details would be beyond the purpose of this thread. Back to your comment: Yes, at any time all layers but one are hidden to save screen space. Furthermore, the Find in Page script stops at the first match and then scrolls the matched text into view and highlights it. A next Find request finds the next match on the page, and so on.

What I'm looking for is a way to detect within which layer(-id) a match takes place and then change the property of that layer from "hide" to "show".

Hope this clarifies my problem and helps finding a solution.

Peter
 
I could be wrong about this ... BUT If I understand what you want correctly, there are conflicting requirements, and a technical impasse.

When you select a text range and scroll into view ... this will not work intuitively on invisible text:

range.select();
range.scrollIntoView();

It should throw an error. Assuming that you don't get a MS IE alert thrown (about unable to do operation...) how are you going to show the text? Select operates on visible text and highlighhts it. When the user clicks a button to do so, the text will lose focus. If you make one layer visble, the other one disappears (the search string could be found in both)

I think you may want to split the work:

(a) search on the visible area only

(b) separate strategy for invisible areas:

i.e. just tell the user that the text is found in an invisible layer...

Now the technical issue: Selecting the text range to include an invisible layer... just doesnt add up to me. If it works ... it cannot ever be focussed like select is supposed to do.

If I am on the right track, can I suggest something else to handle invisible areas.

EXCLUDE the invisible content from the main search

Maybe you could scan them separately (even using another technique perhaps, as innerText) and report that invisible matches exist to the user, prompting him to let you make it visible and then do the select ONLY in once it is visible.







 
Friendlyposter, fortunately range.select(); and range.scrollIntoView(); does NOT throw an error and the highlight remains even when the user selects another layer. For a demo, have a look at my test site at Enter "333" in the search field and click the red arrow. Now click the third image in the left column (note: the onClick event first hides all layers and then sets the selected layer to "show") and see how the page has been scrolled and that the search text is still highlighted.

So, the only thing I have/want to add to my script is a means to detect in which layer the match took place, and then make that layer visible to the user.
 
I am unfamiliar with using textrange.
Can you easily check x number of characters preceding or following the match that you find in the textrange?
If you can then you can read a number of characters around the match then search the innerTEXT of your hidden divs to determine which one has that exact match and make it visible.

My only other suggestion would be to loop through all page elements, testing their type and searching the innerTEXT or innerHTML property where appropriate to find the text match and thereby identify which element contains the match. You may still have to create a textrange to do the highlighting though.


It's hard to think outside the box when I'm trapped in a cubicle.
 
Nightowl, your suggestion to use the innerHTML property put me on the right track. It took some time to figure out how to do that, but the script now runs flawlessly. Have a look at
For all those who now or in the future want to use the script, here it is:

Note: Written for IE only, browser compatibility yet to be tested.

Have fun, Peter

Code:
// Copyright Peter van der Vliet, 2006

// This script finds text in a page containing layers on top of each other.
// The matched text is scrolled into view and highlighted and the layer 
// containing the text is set to 'visible'.

// Assumption: Only Layer DIV's have an ID.

var n = 0;
var prevStr = "";
var rangeMatchCnt, layerMatchCnt;

function findInLayers(str)
{
  var startPos = 0;
  var i, found;
  if (str != prevStr)     // If new string
  {
    prevStr = str;
    rangeMatchCnt = 0;
    layerMatchCnt = 0;
    n = 0;
  }

  var range = window.document.body.createTextRange();

  // Find the nth match from the top of the page
  for (i = 0; i <= n && (found = range.findText(str)) != false; i++)
  {
    range.moveStart("character", 1);
    range.moveEnd("textedit");
  }

  // If found, mark it and scroll it into view.
  if (found)
  {
    range.moveStart("character", -1);
    range.findText(str);
    range.select();
    range.scrollIntoView();
    n++;
    rangeMatchCnt++;
    layerMatchCnt = rangeMatchCnt;

    // Find the nth match from the top of the layer
    for (num = 0; num < document.getElementsByTagName('div').length; )
    {
      oLayer = document.getElementsByTagName('div')[num];
      if (oLayer.id != '') // If Layer-id (all other DIV's should not have an ID)
      {
        var stringPos = document.getElementById(oLayer.id).innerHTML.toLowerCase().indexOf(str.toLowerCase(), startPos);
        if (stringPos != -1) // If match
        {
          startPos = stringPos + 1;
          if (--layerMatchCnt == 0) // If nr of matches in layers equals nr of matches in page
          {
            switchLayer(oLayer);
            break;
          }
        }
        else
        {
          ++num;
          startPos = 0;
        }
      }
      else
      {
        ++num;
        startPos = 0;
      }
    }
  }

  // Otherwise, display message.
  else
  {
    if (n > 0) // Previous match was the last one
    {
      n = 0;
      rangeMatchCnt = 0;
      alert("This was the last match.");
    }

    // Not found anywhere, give message.
    else
    {
      alert("Search text not found.");
    }
  }

  return false;
}


function switchLayer(layerId) //set all layers hidden and layer 'layerId' visible
{
  for (nr = 0; nr < document.getElementsByTagName('div').length; ++nr)
  {
    objLayer = document.getElementsByTagName('div')[nr];
    if (objLayer.id != '')
    {
      objLayer.style.visibility = 'hidden';
    }
  }
  layerId.style.visibility = 'visible';
}
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top