Basically I have a html page with hundreds of images on it each with a title attribute describing the image. Ideally I would change all this but the page has to stay as it is for now.
I want to search these title attributes and scroll the page to the corresponding image if possible. - I've played around with some javascript search scripts but cannot get it to work with straightforward "On page" searches as the tags are in the code rather than displayed on page.
Can anyone point me in the right direction on how to do something like this?
This was the "Search on page" code I was using
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 (window.find) {
// Look for match starting at the current point. If not found, rewind
// back to the first match.
if (!window.find(str)) {
while (window.find(str, false, true)) {
n++;
}
} else {
n++;
}
// If not found in either direction, give message.
if (n == 0) {
alert("Not found.");
}
} else if (window.document.body.createTextRange) {
txt = window.document.body.createTextRange();
// Find the nth match from the top of the page.
found = true;
i = 0;
while (found === true && i <= n) {
found = txt.findText(str);
if (found) {
txt.moveStart("character", 1);
txt.moveEnd("textedit");
}
i += 1;
}
// If found, mark it and scroll it into view.
if (found) {
txt.moveStart("character", -1);
txt.findText(str);
txt.select();
txt.scrollIntoView();
n++;
} else {
// Otherwise, start over at the top of the page and find first match.
if (n > 0) {
n = 0;
findInPage(str);
}
// Not found anywhere, give message. else
alert("Not found.");
}
}
return false;
}
You can select by html attribute.
Using plain JS (in modern browsers incl. IE8+):
document.querySelectorAll('[title*="my text"]')
Using jQuery:
$('[title*=my text]')
would find:
<img src="/path" title="this is a title with my text" />
From there, you would need to get the page position of the image returned by the selector, and then scroll your page to that point, optionally (likely) with some offset so it doesn't bang up against the top of the viewport
EDIT:
function findElementsByTitle(str) {
return document.querySelectorAll('[title*="' + str + '"]');
}
function scrollToElement(el) {
var yOffset = el.offset().top; //this is a jQuery method...you don't want to write this in plain JS
window.scrollTo(0, yOffset - 10) //params are x,y. the - 10 is just so your image has some "padding" in the viewport
}
Related
As the title says, is there any function or simple method to calculate pixel distance between the top of a div and the top of the HTML document? All the questions I've seen on stackoverflow do it for top of div and top of viewport which is different than what I need. Thanks in advance.
Here is a function you could use, but I'm not sure it works on all contexts :
function topRelativeToDocumentElement(element) {
var curCss, t=0;
while (element!==null && element!==document.documentElement) {
if ((defaultView=element.ownerDocument.defaultView) && (computedStyle=defaultView.getComputedStyle(element, null))) {
curCss=computedStyle.getPropertyValue("position");
} else {
curCss=null;
}
if (curCss!=="static" && curCss!=="fixed") {
t+=element.offsetTop;
} else if (curCss==="fixed") {
t+=element.offsetTop;
break;
}
if (typeof(element.parentNode)!=="undefined") {
element=element.parentNode;
} else {
break;
}
}
return t;
}
[Edit]
I would advise to use this instead :
var top=elem.getBoundingClientRect().top - document.documentElement.getBoundingClientRect().top
The first function takes not in consideration css transforms, the second one does.
I have a web page on which my sidebar links will cause an 'external' HTML document to be loaded into a content div.
However after it is successfully loaded and displayed, the loaded HTML content does not appear in the Page Source.
Regardless, I now need to do a client-side Text Search of that 'external' HTML document using a Javascript function.
My webpage looks like the following:
The Search textbox and button are 'outside' of the Content Div (bordered in Red).
And, at the time that one of the link's HTML documents is appearing on-screen the page source looks like:
<!-- Page Content -->
<div id="page-content-wrapper" style="border: thick solid #FF0000; height:660px">
<!--Loaded content goes here-->
</div>
Notice that the 'loaded' HTML document is not showing.
I have found a Javascript function findInPage() which looks promising, but it is not finding the 'loaded' HTML document and its text.
// =====================================
function findInPage() {
var str = document.getElementById("ButtonForm").elements["txtSearch"].value;
var n = 0;
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 (window.find) {
// Look for match starting at the current point. If not found, rewind
// back to the first match.
if (!window.find(str)) {
while (window.find(str, false, true))
n++;
} else {
n++;
}
// If not found in either direction, give message.
if (n == 0)
alert("Not found.");
} else if (window.document.body.createTextRange) {
txt = window.document.body.createTextRange();
// Find the nth match from the top of the page.
found = true;
i = 0;
while (found === true && i <= n) {
found = txt.findText(str);
if (found) {
txt.moveStart("character", 1);
txt.moveEnd("textedit");
}
i++;
}
// If found, mark it and scroll it into view.
if (found) {
txt.moveStart("character", -1);
txt.findText(str);
txt.select();
txt.scrollIntoView();
n++;
} else {
// Otherwise, start over at the top of the page and find first match.
if (n > 0) {
n = 0;
findInPage(str);
}
// Not found anywhere, give message. else
alert("Not found.");
}
}
return false;
}
Is there some way to modify the function and/or use a different function such that it can find the 'loaded' HTML document and search it for the entered Text?
try selecting the div by id instead of reading the whole window...
document.getElementById('page-content-wrapper').find(str)
This is a duplicate of How to get html elements from an object tag?.
The answer lies in replacing all instances of window.document with document.getElementById('page-content-wrapper').contentDocument, such that the searched document is the page document.
However, the search function you have there is quite broken, it depends on window.find to search the window instead of searching the document text.
You could build a better search function:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Wrapper</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
Body Here
<object id="page-content-wrapper" type="text/html" data="about.html" name="content"></object>
<input id="txtSearch" placeholder="Search..." />
<button onclick="findInPage()">Search</button>
<script>
function findInPage() {
var needle = document.getElementById('txtSearch').value;
var haystack = document.getElementById('page-content-wrapper').contentDocument.body.innerHTML;
var match = haystack.indexOf(needle);
if(match != -1) {
console.log(match);
} else {
console.log('Not Found');
}
}
</script>
</body>
</html>
Fill about.html with anything you want to load and search in.
Note that this just logs the index of the match. It doesn't scroll it into view or select it. With a little more javascript you can do those though. You just have to go through document.getElementById('page-content-wrapper').contentDocument.body
I finally found how to access the HTML content of the Object's innerHTML.
Upon clicking on a specific Sidebar link, using Javascript, I build its Object.
function MenuClick(doc) {
var Tutorial = doc;
var DisplayObj = "<object id='ThisObj' type='text/html' data='" + Tutorial + "' style='min-width:100%; min-height: 101%; overflow: hidden'></object>";
document.getElementById("page-content-wrapper").innerHTML = DisplayObj;
}
But now I have added an ID to the Object ('ThisObj').
With that ID now defined I was able to 'drill-down' into that Object to get to its innerHTML.
var sourceObj = document.querySelector("#ThisObj");
var sourceBody = sourceObj.contentDocument.body
var haystack = sourceBody.innerHTML;
var match = haystack.indexOf(needle);
if (match != -1) {
// --- Text match found ---
} else {
// --- Text match NOT found ---
}
I still need to create the Javascript to highlight the 'found' text and scroll it into view but I'll ask that question in a separate post.
Thanks for your suggestions/advice.
I've put together a small test at http://jsfiddle.net/Hwqb3/3/ this morning. This is on the back of a larger project with pagination. I have tried this with native JS and jQuery. The test uses jQuery.
A quick search on SO says that Chrome handles things poorly if background-size is set, but this is not the case here. No trace of background-size in the source, and inspecting elements shows no background-size being set / inherited.
Ignore the initial page load while 5,000 elements are added to the list. It is only a few seconds, but it just so there are some elements to test with.
In Firefox 18.0.1, the moving between pages is almost instant and in IE9 there is maybe a 0.1s delay between mouse click and the paged results refreshing; However, in Chrome (24.0.1312.57 m) the delay is a noticeable 1-2 seconds.
I spent the majority of my night last night pouring over my code to see if I can find the cause before writing this test. This is bare bones and still has the issue.
I can only assume that Chrome is handling the element.style.display=''; poorly. Without that (even looping through the 5,000 elements to display='none') the thing is snappy.
Any ideas? Client wants pagination on a result set of around 4,000 - 7,500, but doesn't want page reloads and doesn't understand that they should apply filters to whittle that list down to <100, as no one is ever going to page through 200 - 375 pages looking for something specific.
Last resort is AJAX calls, which may be slightly quicker on Chrome. Untested yet though.
Thanks in advance.
Code from jsfiddle, excluding the jQuery CDN link
HTML:
First
Previous
Next
Last
<br>
<ul id='list'>
</ul>
JS:
window.onload=function() {
window.list=$('#list'), window.max=20, window.page=0, window.pages=0, window.elements;
var i=0;
while(i<5000) {
i++;
list.append("<li>"+i+"</li>");
}
jump('first');
};
function jump(operation) {
window.elements=list.find('li');
window.pages=Math.ceil(window.elements.length/window.max);
if(operation=='first') {
window.page=0;
}
else if(operation=='last') {
window.page=(window.pages-1);
}
else if(operation=='+1') {
window.page=(window.page+1);
if(window.page>=window.pages) {
window.page=(window.pages-1);
}
}
else if(operation=='-1') {
window.page=(window.page-1);
if(window.page<0) {
window.page=0;
}
}
var showing=0, total=0;
window.elements.each(function() {
var show=false, self=$(this);
if(showing<window.max) {
if(total>=(window.page*window.max) && total<((window.page*window.max)+window.max)) {
self[0].style.display='';
showing++;
show=true;
}
}
if(!show) {
self[0].style.display='none';
}
total++;
});
}
check this
window.onload = function() {
window.list = $('#list'),
window.max = 20,
window.page = 0,
window.pages = 0,
window.elements;
var i = 0;
var html = '';
while(i < 5000) {
i++
html += '<li>' + i + '</li>';
}
list.append(html);
window.elements = list.find('li');
window.pages = Math.ceil(window.elements.length/window.max);
jump('first');
};
function jump(operation) {
if (operation == 'first')
window.page = 0;
else if (operation == 'last')
window.page = window.pages - 1;
else if (operation == '+1')
(window.page + 1 >= window.pages) ? window.page = window.pages - 1 : window.page++ ;
else if (operation == '-1')
(window.page - 1 < 0) ? window.page = 0 : window.page--;
var index = page * window.max;
window.elements.hide().slice(index, index + window.max).show();
}
http://jsfiddle.net/Hwqb3/16/
I'm testing out a layout on a website using 3 pictures here:
Schechterbusiness.info
the left button works, making it go through the pictures. But I can't figure out how to get the right button to work, which is supposed to scroll through the other way. I know there's probably a way to do it with arrays but I can't wrap my brain around it. Halp!
Code to scroll through pictures:
$('#fryLink').click(function() {
$('#hide').hide();
$('#img').hide();
count++;
if(count == 1) {
$('#img').attr("src","images/fry.png");
}
else if(count == 2) {
$('#img').attr("src","images/bender.png");
}
else if(count == 3) {
$('#img').attr("src","images/zoidberg.png");
}
$('#img').show("fade");
if(count > 2) {
count = 0;
}
normally it should be enough to just use use the same function the other way around with
count--;
following the first answere
Bind event to right mouse click
reverse the counter ;)
You have count cycling through four states: 0, 1, 2, and 3. But you only set the image for states 1 - 3.
This leads to duplicating one image--whichever was the last one--when your count variable is on 0.
As to helping you get exactly what you want, unfortunately that is not really clear. Are the three buttons supposed to be a sort of "forward / something / reverse"? What do you want to happen when the center button is clicked on?
Also, making the buttons display the proper pointer/hand icon is important. Right now they show the text selection bar instead, and that makes it confusing since it conveys to the user that the items are not clickable.
Try this
var count = 0;
var imgLength = 3;
$('#leftLink , #rightLink').click(function() {
$('#hide').hide();
$('#img').hide();
if (this.id === 'leftLink') {
count--;
if (count < 0) {
count = imgLength-1;
}
}
else {
count++;
if (count > imgLength-1) {
count = 0;
}
}
var src = "images/fry.png";
if (count == 1) {
src = "images/bender.png";
}
else if (count == 2) {
src = "images/zoidberg.png";
}
$('#img').attr('src',src).show("fade");
});
JS beginner here;
Ok, I'm trying to manipulate the functions of Codaslider for a layout. What I need is the ability to use an image for slide dynamic slide navigation.
I've solved the issue for dynamic hashing, however I'm stuck at modifying the HTML. I've tried a few things but I figure this is the easiest way...
This is what I've got so far;
function navigate ()
{
var url = document.getElementById('back');
url.href = page_back();
return url;
}
function page_back(inward)
{
new Object(inward.location.hash);
var rehash = inward.location.hash.match(/[^#]/);
if (rehash == 1) {
rehash = 5;
}
else if(rehash == 2) {
rehash = 1;
}
else if(rehash == 3) {
rehash = 2;
}
else if(rehash == 4) {
rehash = 3;
}
else if(rehash == 5) {
rehash = 4;
}
else if(rehash == null) {
rehash = 5;
}
else{rehash = "Invalid URL or REFERRER"}
inward.location.hash = rehash;
return inward.location.href;
};
Implemented here;
<a href="#5" id="back" class="cross-link"> <input type="image" class="left_arrow" src=
"images/leftarrow.png" onclick="navigate()" /></a>
What I expect this to do is change the href value to "#1" so that Codaslider will do it's thing while I provide a stationary dynamic image for slide browsing.
Anyone have any idea what i'm doing wrong? page_back works fine but navigate seems to be useless.
sup Josh
so to start this line here
new Object(inward.location.hash);
Unless i completely missed some javascript weirdness that line should not do any thing
the function
function page_back(inward)
takes a inward argument but you call it from navigate without an argument
url.href = page_back();
ps. the location object can be found on window.location
happy coding :D