We're getting a really weird issue where it's seemingly impossible to get the correct position and size of an element on screen. The code works perfectly on Android but the values are always too large on iOS, typically returning more than half the space on the screen for an element that takes less than a third. The values returned are typically lower down the screen than the element, and much taller than the element. This is after dividing by devicePixelRatio.
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<style>
#text-container > p {
text-align: justify;
transition: background-color 1s;
font-size: 3vh;
}
.selected {
background-color: rgba(253, 230, 65, 0.5);
}
</style>
<script src="jquery.min.js"></script>
<script src="jquery.scrollTo.min.js"></script>
<!-- Custom double-tap detector, couldn't get other solutions to work -->
<script>
function onElementsDoubleTap($els, callback) {
var doubleTapThresholdMs = 250;
var asArray = [];
for (var i = 0; i < $els.length; i++)
asArray.push($els[i]);
asArray.forEach(function (el) {
var lastTouchTime = Date.now();
el.addEventListener('touchstart', function (e) {
if (e.touches > 1)
return;
var touchTime = Date.now();
if (touchTime.valueOf() - lastTouchTime.valueOf() < doubleTapThresholdMs) {
callback(el);
}
lastTouchTime = touchTime;
});
});
}
</script>
<!-- Data templates -->
<script id="paragraph-template" type="text/template">
<p class="para-<%= index %>">
<% _.each(words, function (wordObj) { %>
<span class="in-paragraph-<%= wordObj.paragraphIndex %> in-sentence-<%= wordObj.sentenceIndex %> word-<%= wordObj.index %>"><%= wordObj.word %></span>
<% }); %>
</p>
</script>
</head>
<body>
<div id="text-container"></div>
<!-- Generate DOM elements using templates -->
<script>
function Generate(textAdapter) {
var paragraphTemplate = _.template($('#paragraph-template').html());
var container = $('#text-container');
Paragraphs.forEach(function (paragraph) {
container.append(paragraphTemplate(paragraph));
});
var $sections = $('p');
console.log($sections.length);
onElementsDoubleTap($sections, function (s) {
selectAndScrollTo($(s), function () {
csInvokeAction('userSelectedWord', $(s).attr('class'));
console.log('Capturing bounds for class=' + $(s).attr('class'));
console.log('Offset=' + JSON.stringify($(s).offset()));
console.log('Position=' + JSON.stringify($(s).position()));
var sectionPos;
setTimeout(function () {
var bounds = s.getBoundingClientRect();
console.log('Raw bounds = ' + JSON.stringify(bounds));
sectionPos = {};
//sectionPos.left = bounds.left / (window.devicePixelRatio * 1.5);
//sectionPos.right = bounds.right / (window.devicePixelRatio * 1.5);
//sectionPos.top = bounds.top / (window.devicePixelRatio * 1.5);
//sectionPos.bottom = bounds.bottom / (window.devicePixelRatio * 1.5);
var position = $(s).position();
position.top -= scrollTop;
sectionPos.left = position.left / window.devicePixelRatio;
sectionPos.top = position.top / window.devicePixelRatio;
sectionPos.right = position.left + ($(s).innerWidth() / window.devicePixelRatio);
sectionPos.bottom = position.top + ($(s).innerHeight() / window.devicePixelRatio);
console.log('Using sectionPos=' + JSON.stringify(sectionPos));
var dataString = '';
dataString += sectionPos.top + ',';
dataString += sectionPos.right + ',';
dataString += sectionPos.bottom + ',';
dataString += sectionPos.left;
csInvokeAction('readerSectionCaptured', dataString);
}, 5000);
});
});
}
function selectAndScrollTo($el, callback) {
$('.selected').removeClass('selected');
$el.addClass('selected');
var windowHeight = window.innerHeight;
$(window).scrollTo($el, {
offset: {
'top': -windowHeight * 0.5
},
onAfter: callback,
duration: 250
});
}
</script>
</body>
</html>
(Incomplete code)
We've tried getBoundingClientRect, getting the data from jQuery, delaying calls until scroll is complete, and taking devicePixelRatio into account. At this point the only thing I can seem to do is apply some magic numbers (ie multiply devicePixelRatio by 1.5) to get the element size somewhere in the ballpark.