UPDATE 4 Hi @Paul. I think I know what's happening, I just don't know how to fix it. The Alert I had in touchstart was causing the app to stop and when I clicked OK the touchend event had already passed. I removed the touchstart Alert and the touchend alert worked, the istouch value was "true". I added a few more alerts to see where it is failing and found that math.abs(e.pageX) was not a number - the alert showed NaN. Also $snd.data('tx') showed as "Undefined". Because of this vx was also reported as NaN. The value for ds displayed a numeric value.
So, I think the problem is that $snd is defined in .on(touchstart...) and is not referencable from .on(touchend...). Could this be a scope issue for the variables? At least the original terms and the search results that are copied into the search results page are showing the same symptoms so the onclick handler is being triggered which is good progress. Below is the current version of the click handlers, could you let me know what I should change, I'm quite new at js and this has been really challenging for me. Thanks again.
$(".wrapper") //i'm attaching all event handlers to body, so everything with .spanish class will have them attached
.on("click", ".spanish", function(e) {
e.preventDefault();
})
.on("touchstart", ".spanish", function(e) {
if (istouch) {
var $snd = $(this);
$snd.data({
tstart: new Date().getTime(),
tx: e.pageX,
ty: e.pageY
});
}
})
.on("touchend", ".spanish", function(e) {
if (istouch) {
alert("touchend detected istouch val = " + istouch); // shows istouch = true
var $snd = $(this);
var snd = this;
var ds = $snd.data('tstart');
alert("ds = " + ds); // shows a numeric value
if (!ds) {
return;
}
alert("math.abs e.pagex = " + Math.abs(e.pageX)); // shows NaN
alert("$snd.data('tx') = " + $snd.data('tx')); // shows "undefined"
var vx = Math.abs(e.pageX - $snd.data('tx'));
alert("vx = " + vx); // shows NaN
var vy = Math.abs(e.pageY - $snd.data('ty'));
Math.abs(e.pageY - $snd.data('ty'));
alert("vx= "+ vx +" vy= " + vy + " ds = " + ds);
if (vx < 20 && vy < 20 && new Date().getTime() - ds < 400) {
alert("tap detected and about to call playstop");
playstop.apply(snd, [e]);
$snd.data({
tstart: null,
tx: null,
ty: null
});
}
}
});
});
UPDATE 3
Hi @Paul, I've made a minor change to your code, I applied the click handler to the .wrapper
div instead of Body
because there's a home page with links that stopped working when I applied your update, the home page links work OK now.
Each English phrase can be translated into 1,2 or 3 Spanish phrases, all of the Spanish phrases are contained within a single .wrapper div for any given English div.
Below is a sample showing 3 English divs with their Spanish translations. I have 800+ English phrases altogether. When the user searches these phrases the complete 'english' div, including the wrapper
div, should be copied to the search results page and it looks like it is being copied correctly.
<div class="english">
How old are you?
<div class="wrapper">
<a class="formal spanish" data-ignore="true" href="ageHowOldF.mp3">F: Cuántos años tiene?</a>
<a class="informal spanish" data-ignore="true" href="ageHowOldI.mp3">I: Cuántos años tienes?</a>
</div> <!-- End .wrapper -->
</div> <!-- End English -->
<div class="english">
When were you born?
<div class="wrapper">
<a class="formal spanish" data-ignore="true" href="ageWhenBornF.mp3">F: Cuando nació?</a>
<a class="informal spanish" data-ignore="true" href="ageWhenBornI.mp3">I: Cuando naciste?</a>
</div> <!-- End .wrapper -->
</div> <!-- End English -->
<div class="english">
How old was he?
<div class="wrapper">
<a class="spanish" data-ignore="true" href="ageHowOldMale.mp3">Cuántos años tenía él?</a>
</div> <!-- End .wrapper -->
</div> <!-- End English -->
UPDATE 2 @Paul, as described in my comment below your reply, please see below the full document ready script with your changes and the searchApp script that matches/ copies the selected element to an empty div.
Here is the document ready script:
jQuery(function($){
var highlight = 'yellow', origcolor = 'transparent', curSnd = null,
istouch = !!('ontouchstart' in window) || !!('ontouchstart' in document.documentElement) || !!window.ontouchstart || (!!window.Touch && !!window.Touch.length) || !!window.onmsgesturechange || (window.DocumentTouch && window.document instanceof window.DocumentTouch);
function playstop(e){
alert("Start playstop");
e.preventDefault();
var $this = $(this);
if(curSnd && curSnd.sound){
if(this === curSnd.tag){
curSnd.sound.stop();
return;
}
curSnd.sound.stop();
}
$this.stop(true, true).css({backgroundColor: highlight});
var filename = this.href.substring(this.href.lastIndexOf('/')), myMedia = new Media(
this.href,
function() {
myMedia && myMedia.release();
curSnd = myMedia = null;
$this.stop(true, true).animate({backgroundColor: origcolor}, 500);
},
function(e) {
myMedia && myMedia.release();
curSnd = myMedia = null;
$this.stop(true, true).animate({backgroundColor: origcolor}, 500);
window.console && console.log ("Audio play error - " + filename + "\ncode: " + e.code + "\nmessage: " + e.message);
}
);
alert("Start playing sound")
curSnd = {tag: this, sound: myMedia};
curSnd.sound.play();
} // End playstop
$("body") //i'm attaching all event handlers to body, so everything with .spanish class will have them attached
.on("click", ".spanish", function(e) {
e.preventDefault();
})
.on("touchstart", ".spanish", function(e) {
if (istouch) {
var $snd = $(this);
$snd.data({
tstart: new Date().getTime(),
tx: e.pageX,
ty: e.pageY
});
}
})
.on("touchend", ".spanish", function(e) {
if (istouch) {
var $snd = $(this);
var snd = this;
var ds = $snd.data('tstart');
if (!ds) {
return;
}
var vx = Math.abs(e.pageX - $snd.data('tx'));
var vy = 'enter code here';
Math.abs(e.pageY - $snd.data('ty'));
if (vx < 20 && vy < 20 && new Date().getTime() - ds < 400) {
playstop.apply(snd, [e]);
$snd.data({
tstart: null,
tx: null,
ty: null
});
}
}
});
});
</script>
Here is the search/Copy script
function searchApp() {
var $searchTerm = $(".searchField").val().toLowerCase(); // Convert search term to all lower case
var $searchResults = "";
document.getElementById("searchResultsDiv").innerHTML = "";
$(".english").each(function () {
if ($(this).text().toLowerCase().match($searchTerm)) { // If this specific '.english' class
$(this).clone(true, true).appendTo("#searchResultsDiv");
}
});
}; // /searchApp
UPDATE
I think I've localized the area that is causing my problem - which is when I copy an element to an empty div in a search results page the event listener from the original element is not being triggered when I tap on the search results page.
Below is a sample English/ Spanish element that is being copied:
<div class="english">
How old are you?
<div class="wrapper">
<a class="formal spanish" data-ignore="true"
href="ageHowOldF.mp3">F: Cuántos años tiene?</a>
<a class="informal spanish" data-ignore="true"
href="ageHowOldI.mp3">I: Cuántos años tienes?</a>
</div> <!-- End .wrapper -->
</div> <!-- End English -->
and here's the code that copies the original element when it finds a match on the English text:
if ($(this).text().toLowerCase().match($searchTerm)) {
$(this).clone(true, true).appendTo("#searchResultsDiv");
}
The English/Spanish phrases correctly appear on the search results page but do not respond to a tap. I added an alert message to the event listener and this does not display when I tap on the search page but does display when i tap on the original page.
I would really appreciate any suggestions you may have. Thanks.
END UPDATE
I have a smartphone app that is a talking English/Spanish phrasebook, when the user taps a Spanish phrase a short mp3 file is played by jQuery
and this is working correctly, the jQuery player is assigned at document ready. I have now created a Search function which copies all matching English/Spanish phrases to a search results page and this also works correctly.
The problem I’m having is when the user taps any of the Spanish phrases on the search results page the app invokes the system sound player (I think) and not the jQuery
sound player and the system sound player displays an audio control bar, which I don’t want my users to see.
I have tried everything I can think of, basically following two approaches:
- Copy all the attributes of the element
- Assign an onclick event to a parent element in the search results page
Neither of these approaches fixes this – at least nothing I know of fixes this.
I’m pretty new to js/jQuery so please forgive me if I’ve missed something blindingly obvious.
Please find below:
- A sample English/Spanish element, there are several hundred of these altogether. These are copied to the search results page if the search matches the English phrase/word
- The document ready script that assigns the click handler to the Spanish phrases
- The search function that finds and copies matching elements to the search results page
SAMPLE English/ Spanish element
<div class="english">
How old are you?
<div class="wrapper">
<a class="formal spanish" data-ignore="true"
href="ageHowOldF.mp3">F: Cuántos años tiene?</a>
<a class="informal spanish" data-ignore="true"
href="ageHowOldI.mp3">I: Cuántos años tienes?</a>
</div> <!-- End .wrapper -->
</div> <!-- End English -->
Document ready script
This also does things like changing the background color, checking for a tap vs. a swipe etc.
jQuery(function($) {
var highlight = 'yellow',
origcolor = 'transparent',
curSnd = null,
istouch = !!('ontouchstart' in window) || !!('ontouchstart' in document.documentElement) || !!window.ontouchstart || (!!window.Touch && !!window.Touch.length) || !!window.onmsgesturechange || (window.DocumentTouch && window.document instanceof window.DocumentTouch);
function playstop(e) {
e.preventDefault();
var $this = $(this);
if (curSnd && curSnd.sound) {
if (this === curSnd.tag) {
curSnd.sound.stop();
return;
}
curSnd.sound.stop();
}
$this.stop(true, true).css({
backgroundColor: highlight
});
var filename = this.href.substring(this.href.lastIndexOf('/')),
myMedia = new Media(
this.href,
function() {
myMedia && myMedia.release();
curSnd = myMedia = null;
$this.stop(true, true).animate({
backgroundColor: origcolor
}, 500);
},
function(e) {
myMedia && myMedia.release();
curSnd = myMedia = null;
$this.stop(true, true).animate({
backgroundColor: origcolor
}, 500);
window.console && console.log("Audio play error - " + filename + "\ncode: " + e.code + "\nmessage: " + e.message);
}
);
curSnd = {
tag: this,
sound: myMedia
};
curSnd.sound.play();
} // End playstop
$('.spanish').click(function(e) {
e.preventDefault();
}).each(function(i, snd) {
if (istouch) {
var $snd = $(snd);
snd.addEventListener('touchstart', function(e) {
$snd.data({
tstart: new Date().getTime(),
tx: e.pageX,
ty: e.pageY
});
}, false);
snd.addEventListener('touchend', function(e) {
var ds = $snd.data('tstart');
if (!ds) {
return;
}
var vx = Math.abs(e.pageX - $snd.data('tx')),
vy = `enter code here`
Math.abs(e.pageY - $snd.data('ty'));
if (vx < 20 && vy < 20 && new Date().getTime() - ds < 400) {
playstop.apply(snd, [e]);
$snd.data({
tstart: null,
tx: null,
ty: null
});
}
}, false);
}
});
});
Search function
function searchApp() {
// Convert search term to all lower case
var $searchTerm = $(".searchField").val().toLowerCase();
var $searchResults = "";
document.getElementById("searchResultsDiv").innerHTML = "";
$(".english").each(function() {
// If this specific '.english' class
if ($(this).text().toLowerCase().match($searchTerm)) {
// Append all HTML contents of '.english' div into $searchResults variable
$searchResults += $(this)[0].outerHTML;
}
});
// Within searchResultsDiv tag, write $searchResults variable to HTML
$("#searchResultsDiv").html($searchResults);
}; // /searchApp
UPDATE 5 I changed the alerts to console.log. First test was to tap a Spanish phrase that already existed on the search results page. Below is the console log
touchstart $snd = [object Object] index.html:67
touchend detected istouch val = true index.html:72
touchend ds = 1427885920974 index.html:76
pageX in touchend = undefined index.html:77
touchend math.abs e.pagex = NaN index.html:82
touchend $snd.data('tx') = undefined index.html:83
touchend vx = NaN index.html:85
touchend vx= NaN vy= NaN ds = 1427885920974
Second test is tapping one of the created Spanish elements. Here's the log
touchstart $snd = [object Object] index.html:67
touchend detected istouch val = true index.html:72
touchend ds = 1427885920974 index.html:76
pageX in touchend = undefined index.html:77
touchend math.abs e.pagex = NaN index.html:82
touchend $snd.data('tx') = undefined index.html:83
touchend vx = NaN index.html:85
touchend vx= NaN vy= NaN ds = 1427885920974
Neither of these played a sound. Below is the touchstart and touchend handler scripts showing where I added the console.log statements.
.on("touchstart", ".spanish", function(e) {
if (istouch) {
$snd = $(this);
$snd.data({
tstart: new Date().getTime(),
tx: e.pageX,
ty: e.pageY
});
console.log("touchstart $snd = " + $snd);
}
})
.on("touchend", ".spanish", function(e) {
if (istouch) {
console.log("touchend detected istouch val = " + istouch);
var $snd = $(this);
var snd = this;
var ds = $snd.data('tstart');
console.log("touchend ds = " + ds);
console.log("pageX in touchend = " + $snd.data('tx'));
if (!ds) {
return;
}
console.log("touchend math.abs e.pagex = " + Math.abs(e.pageX));
console.log("touchend $snd.data('tx') = " + $snd.data('tx'));
var vx = Math.abs(e.pageX - $snd.data('tx'));
console.log("touchend vx = " + vx);
var vy = Math.abs(e.pageY - $snd.data('ty'));
Math.abs(e.pageY - $snd.data('ty'));
console.log("touchend vx= "+ vx +" vy= " + vy + " ds = " + ds);
if (vx < 20 && vy < 20 && new Date().getTime() - ds < 400) {
console.log("touchend tap detected and about to call playstop");
playstop.apply(snd, [e]);
$snd.data({
tstart: null,
tx: null,
ty: null
});
}
}
});
});