Copying an element with all it’s attributes - Update - javascript

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
});
}
}
});
});

Main problem in your code is that you're adding event handlers to one set of objects, and not re-adding them to cloned ones. Delegeation of events would be more suitable for you. Change your ${'.spanish') eventHandlers into:
$("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
});
}
}
});
Basically, i just splitted your function into two separate jquery events delegation function. All those handlers will be attached to everything matching selector .spanish in body container. You can modify it for some optimization (if you have some div as a wrapper for all those elements).
jQuery $.on function can bind some events (touchstart, touchend and click int his case) to current existing elements, and those which will be created in future, for example in process of cloning elements - it will probably be the best solution for you.
Read more about it here: http://api.jquery.com/on/

Related

Extending Touch EventListener to Additional DOM Element

I used a Codrops article/experiment to create an interactive environment for a local group to use at their conferences. The problem with this is the default interaction is not very intuitive. The template used Flickity.js and what seems like classie.js to create this sliding interface I am having trouble with.
The page can be found here:
www.eyeconic.tv/ky-ffa/
Issue: The only way to activate the view-full is by clicking on the html element:
<h2 class=".stack-title">
// After the stack is active you should be able to activate the full view by clicking on the first .stack-item used to create the thumbnail below it. This entire div should be clickable. Users are touching everywhere all over the screen and not actually clicking the title for the desired action. I hope this makes sense.
In other words you should be able to click the stack-title and the image below the title of each stack to pull the stack into the full view mode on the screen. Then click the x or anywhere else on the screen to close the full view.
The following is located in main.js and the reference I found to create the events I am referring to.
//
function initEvents() {
stacks.forEach(function(stack) {
var titleEl = stack.querySelector('.stack-title');
// expand/close the stack
titleEl.addEventListener('click', function(ev) {
ev.preventDefault();
if( classie.has(stack, 'is-selected') ) { // current stack
if( classie.has(bodyEl, 'view-full') ) { // stack is opened
var closeStack = function() {
classie.remove(bodyEl, 'move-items');
onEndTransition(slider, function() {
classie.remove(bodyEl, 'view-full');
bodyEl.style.height = '';
flkty.bindDrag();
flkty.options.accessibility = true;
canMoveHeroImage = true;
});
};
// if the user scrolled down, let's first scroll all up before closing the stack.
var scrolled = scrollY();
if( scrolled > 0 ) {
smooth_scroll_to(isFirefox ? docElem : bodyEl || docElem, 0, 500).then(function() {
closeStack();
});
}
else {
closeStack();
}
}
else if( canOpen ) { // stack is closed
canMoveHeroImage = false;
classie.add(bodyEl, 'view-full');
setTimeout(function() { classie.add(bodyEl, 'move-items'); }, 25);
bodyEl.style.height = stack.offsetHeight + 'px';
flkty.unbindDrag();
flkty.options.accessibility = false;
}
}
else if( classie.has(stack, 'stack-prev') ) {
flkty.previous(true);
}
else if( classie.has(stack, 'stack-next') ) {
flkty.next(true);
}
});
titleEl.addEventListener('mouseenter', function(ev) {
if( classie.has(stack, 'is-selected') ) {
canMoveHeroImage = false;
imghero.style.WebkitTransform = 'perspective(1000px) translate3d(0,0,0) rotate3d(1,1,1,0deg)';
imghero.style.transform = 'perspective(1000px) translate3d(0,0,0) rotate3d(1,1,1,0deg)';
}
});
titleEl.addEventListener('mouseleave', function(ev) {
// if current stack and it's not opened..
if( classie.has(stack, 'is-selected') && !classie.has(bodyEl, 'view-full') ) {
canMoveHeroImage = true;
}
});
});
window.addEventListener('mousemove', throttle(function(ev) {
if( !canMoveHeroImage ) return false;
var xVal = -1/(win.height/2)*ev.clientY + 1,
yVal = 1/(win.width/2)*ev.clientX - 1,
transX = 20/(win.width)*ev.clientX - 10,
transY = 20/(win.height)*ev.clientY - 10,
transZ = 100/(win.height)*ev.clientY - 50;
imghero.style.WebkitTransform = 'perspective(1000px) translate3d(' + transX + 'px,' + transY + 'px,' + transZ + 'px) rotate3d(' + xVal + ',' + yVal + ',0,2deg)';
imghero.style.transform = 'perspective(1000px) translate3d(' + transX + 'px,' + transY + 'px,' + transZ + 'px) rotate3d(' + xVal + ',' + yVal + ',0,2deg)';
}, 100));
// window resize
window.addEventListener( 'resize', throttle(function(ev) {
// recalculate window width/height
win = { width: window.innerWidth, height: window.innerHeight };
// reset body height if stack is opened
if( classie.has(bodyEl, 'view-full') ) { // stack is opened
bodyEl.style.height = stacks[flkty.selectedIndex].offsetHeight + 'px';
}
}, 50));
// Flickity events:
flkty.on('cellSelect', function() {
canOpen = false;
classie.remove(bodyEl, 'item-clickable');
var prevStack = stacksWrapper.querySelector('.stack-prev'),
nextStack = stacksWrapper.querySelector('.stack-next'),
selidx = flkty.selectedIndex,
cellsCount = flkty.cells.length,
previdx = selidx > 0 ? selidx - 1 : cellsCount - 1;
nextidx = selidx < cellsCount - 1 ? selidx + 1 : 0;
if( prevStack ) {
classie.remove(prevStack, 'stack-prev');
}
if( nextStack ) {
classie.remove(nextStack, 'stack-next');
}
classie.add(stacks[previdx], 'stack-prev');
classie.add(stacks[nextidx], 'stack-next');
});
flkty.on('dragStart', function() {
canOpen = false;
classie.remove(bodyEl, 'item-clickable');
});
flkty.on('settle', function() {
classie.add(bodyEl, 'item-clickable');
canOpen = true;
});
}
init();
})();
I wrapped the title and the first stack item in a div class .touch-me and it worked fairly well. I had previously tried to do this and received an error. But I may have mistyped something because it only made sense.
ISSUE: It works on mouseclick, but it is not working with touch on windows. I have untested it in any other environment because it will be deployed on a windows touch screen.
Although I cannot tell the layer not to close on touch when you swipe or touch the header image for the stack.... I'm afraid I do not have the skillset to properly modify the logic in the javascript since I do not entirely understand the plugins being used.

Draggable code not working with hammer.js

I've successfully implemented jQueryUI draggable, but as soon as I add hammer.js code, the draggable code no longer works.
It is not as soon as I include hammer.js, but as soon as I use the script.
Why is this? How can I get them both to work?
Both the draggable and hammer are applied to .dataCard and #main
The draggable code works fine here ( with hammer implementation commented out ): http://goo.gl/MO5Pde
Here is an example of the draggable code:
$('#main').draggable({
axis:'y',
revert:true,
start: function(event, ui){
topValue = ui.position.top;
},
drag: function(event, ui){
if(pastBreakpoint === false){
$('#searchInput').blur();
if(topValue > ui.position.top) return false;
if(ui.position.top >= 161){
if(pastBreakpoint === false){
pastBreakpoint = true;
if($('.loadingRefresh').length === 0) $('#main').before('<div class="loadingRefresh"></div>');
else{
$('.loadingRefresh').remove();
$('#main').before('<div class="loadingRefresh"></div>');
}
$('.loadingRefresh').fadeIn();
$('#main').mouseup();
setTimeout(function(){
location.reload();
}, 1000);
}
}
}
}
});
Here is the hammer code uncommented and the draggable code not working: http://goo.gl/994pxF
Here is the hammer code:
var hammertime = Hammer(document.getElementById('main'), {
transform_always_block: true,
transform_min_scale: 0
});
var posX = 0,
posY = 0,
lastPosX = 0,
lastPosY = 0,
bufferX = 0,
bufferY = 0,
scale = 1,
last_scale = 1;
hammertime.on('touch transform transformend', function(ev) {
if ((" " + ev.target.className + " ").indexOf(" dataCard ") < 0) return;
else manageMultitouch(ev, ev.target); });
function manageMultitouch(ev, element) {
switch (ev.type) {
case 'touch':
last_scale = scale;
return;
case 'transform':
scale = Math.min(last_scale * ev.gesture.scale, 10);
break;
}
if(scale <= 0.5) $(element).hide('clip');
if(scale > 1.0) $(element).addClass('focused');
var transform = "translate(" + 0 + "px," + 0 + "px) " + "scale(" + 1 + "," + scale + ")";
var style = element.style;
style.transform = transform;
style.oTransform = transform;
style.msTransform = transform;
style.mozTransform = transform;
style.webkitTransform = transform;
}
I had the same problem in my app, even with touch punch included. I had to do a good research to find what was the problem stopping the jquery ui drag.
The problem occurring is a preventDefault set at the event ( only when hammer is included ) changing the result of a trigger method from jquery ui.
Well, lets get back a little bit:
The first method you should see is the _mouseMove(), which is connected with the mousemove event.
The drag will be trigged only when the condition (this._mouseStart(this._mouseDownEvent, event) !== false) be true.
_mouseMove: function (event) {
// IE mouseup check - mouseup happened when mouse was out of window
if ($.ui.ie && (!document.documentMode || document.documentMode < 9) && !event.button) {
return this._mouseUp(event);
}
if (this._mouseStarted) {
this._mouseDrag(event);
return event.preventDefault();
}
if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
this._mouseStarted =
(this._mouseStart(this._mouseDownEvent, event) !== false);
(this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
}
return !this._mouseStarted;
}
The next method will create the helper ( element's clone ), set some css in the element and return true ( value we expect ), unless this._trigger("start", event) returns false.
_mouseStart: function(event) {
var o = this.options;
//Create and append the visible helper
this.helper = this._createHelper(event);
this.helper.addClass("ui-draggable-dragging");
//Cache the helper size
this._cacheHelperProportions();
//If ddmanager is used for droppables, set the global draggable
if($.ui.ddmanager) {
$.ui.ddmanager.current = this;
}
/*
* - Position generation -
* This block generates everything position related - it's the core of draggables.
*/
//Cache the margins of the original element
this._cacheMargins();
//Store the helper's css position
this.cssPosition = this.helper.css( "position" );
this.scrollParent = this.helper.scrollParent();
this.offsetParent = this.helper.offsetParent();
this.offsetParentCssPosition = this.offsetParent.css( "position" );
//The element's absolute position on the page minus margins
this.offset = this.positionAbs = this.element.offset();
this.offset = {
top: this.offset.top - this.margins.top,
left: this.offset.left - this.margins.left
};
//Reset scroll cache
this.offset.scroll = false;
$.extend(this.offset, {
click: { //Where the click happened, relative to the element
left: event.pageX - this.offset.left,
top: event.pageY - this.offset.top
},
parent: this._getParentOffset(),
relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
});
//Generate the original position
this.originalPosition = this.position = this._generatePosition(event);
this.originalPageX = event.pageX;
this.originalPageY = event.pageY;
//Adjust the mouse offset relative to the helper if "cursorAt" is supplied
(o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
//Set a containment if given in the options
this._setContainment();
//Trigger event + callbacks
if(this._trigger("start", event) === false) {
this._clear();
return false;
}
//Recache the helper size
this._cacheHelperProportions();
//Prepare the droppable offsets
if ($.ui.ddmanager && !o.dropBehaviour) {
$.ui.ddmanager.prepareOffsets(this, event);
}
this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
//If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
if ( $.ui.ddmanager ) {
$.ui.ddmanager.dragStart(this, event);
}
return true;
}
Below is the first _trigger called, its from drag widget.
_trigger: function (type, event, ui) {
ui = ui || this._uiHash();
$.ui.plugin.call(this, type, [event, ui]);
//The absolute position has to be recalculated after plugins
if(type === "drag") {
this.positionAbs = this._convertPositionTo("absolute");
}
return $.Widget.prototype._trigger.call(this, type, event, ui);
}
At this point the result will call another trigger method (this time from the $.Widget) and that's the point where we have the problem.
_trigger: function (type, event, data) {
var prop, orig,
callback = this.options[type];
data = data || {};
event = $.Event(event);
event.type = (type === this.widgetEventPrefix ?
type :
this.widgetEventPrefix + type).toLowerCase();
// the original event may come from any element
// so we need to reset the target on the new event
event.target = this.element[0];
// copy original event properties over to the new event
orig = event.originalEvent;
if (orig) {
for (prop in orig) {
if (!(prop in event)) {
event[prop] = orig[prop];
}
}
}
return !($.isFunction(callback) && callback.apply(this.element[0], [event].concat(data)) === false || event.isDefaultPrevented());
}
return !($.isFunction(callback) && callback.apply(this.element[0], [event].concat(data)) === false || event.isDefaultPrevented());
Our problem is exactly at this line. More specific the || before event.isDefaultPrevented().
When hammer is included the method event.isDefaultPrevented() is resulting true, once the value is denied before return, the final value would be false.
(Without the hammer included the event.isDefaultPrevented() returns false as expected.)
Backing in our _moseMouve(), instead of calling the _mouseDrag() method it'll invoke _mouseUp().
U can see it will unbind the events and call _mouseStop().
_mouseUp: function (event) {
$(document)
.unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
.unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
if (this._mouseStarted) {
this._mouseStarted = false;
if (event.target === this._mouseDownEvent.target) {
$.data(event.target, this.widgetName + ".preventClickEvent", true);
}
this._mouseStop(event);
}
return false;
}
If you change the OR (||) operator by an AND (&&) it'll work fine. Off course it's not a little change, I had been testing it and until this moment I haven't find any problem at all. The line would be like this:
return !($.isFunction(callback) && callback.apply(this.element[0], [event].concat(data)) === false && event.isDefaultPrevented());
As I said, its not 100% secure, but until now I didn't find a reason to keep || instead of &&. I'll keep testing it for a few days.
Besides I've already sent an email to the lead developer from jquery ui asking about it.
Similar problem for me using it with isomorphic smartclient.
I fixed it by handling the start event and resetting isDefaultPrevented to false
$(element).draggable({
start: function (event, ui) {
event.isDefaultPrevented = function () { return false; }
}
});
I had the same problem and found this issue at github.
the shown solution using event delegation worked fine for me:
$(document).hammer().on('touch transform transformend', '#main', function() {...});

How to decode this javascript?

My question is how can I decode this JavaScript and how is encoded (with which program or online tool).
Here is the JavaScript that I want to decode:
http://pastebin.com/hZvKySjj
Every obfuscated script needs some kind of eval. In here, the lines
_L = 'constr\x75\x63\x74\x6F\x72';
[][_L][_L](_Z[_h._t4](_F))();
are doing this. _L is the string "constructor", and [].constructor.constructor is the Function constructor. It will be called with the decoded script, and the resulting function will be called. We can substitute it with an alert, paste the script in the console*, and wait for the result - we don't even need to understand how the decoding works. In your case, the result is (yes, including all the comments and linebreaks):
var alarm ="0";
var content = document;
if ((content.getElementById("wrapper") != null))
{
document.getElementById('wrapper').style.display = 'block';
}
function a ()
{
if ((content.getElementById("links") != null))
{
var temp = content.getElementById("links").innerHTML;
if ((temp.indexOf('nofollow')+1) > 0) alarm = "1";
else if ((temp.indexOf('noindex')+1) > 0) alarm = "1";
}
else alarm = "1";
}
function b ()
{
if ((content.getElementById("aa") != null) && (content.getElementById("ab") != null))
{
temp = document.getElementById("aa").href;
if ("http://uc-portaller.ru/" != temp) alarm = "1";
temp = document.getElementById("ab").innerHTML;
if ("скрипты для ucoz" != temp) alarm = "1";
}
else alarm = "1";
}
function c ()
{
if ((content.getElementById("ba") != null) && (content.getElementById("bb") != null))
{
temp = content.getElementById("ba").href;
if ("http://austere.ru/" != temp) alarm = "1";
temp = content.getElementById("bb").innerHTML;
if ("доска объявлений" != temp) alarm = "1";
}
else alarm = "1";
}
function d ()
{
if ((content.getElementById("ca") != null) && (content.getElementById("cb") != null))
{
temp = content.getElementById("ca").href;
if ("http://www.for-creative.com/" != temp) alarm = "1";
temp = content.getElementById("cb").innerHTML;
if ("темы для ucoz" != temp) alarm = "1";
}
else alarm = "1";
}
a ();
if (alarm == "0") b ();
if (alarm == "0") c ();
if (alarm == "0") d ();
if (alarm == "1") prompt('Нарушены условия использования, по всем вопросам обращайтесь в ICQ:', '376880395');
$(document).ready(function(){
//When you click on a link with class of poplight and the href starts with a #
$('a.poplight[href^=#]').click(function() {
var popID = $(this).attr('rel'); //Get Popup Name
var popURL = $(this).attr('href'); //Get Popup href to define size
//Pull Query & Variables from href URL
var query= popURL.split('?');
var dim= query[1].split('&');
var popWidth = dim[0].split('=')[1]; //Gets the first query string value
//Fade in the Popup and add close button
$('#' + popID).fadeIn().css({ 'width': Number( popWidth ) }).prepend('');
//Define margin for center alignment (vertical + horizontal) - we add 80 to the height/width to accomodate for the padding + border width defined in the css
var popMargTop = ($('#' + popID).height() + 80) / 2;
var popMargLeft = ($('#' + popID).width() + 80) / 2;
//Apply Margin to Popup
$('#' + popID).css({
'margin-top' : -popMargTop,
'margin-left' : -popMargLeft
});
//Fade in Background
$('body').append('<div id="fade"></div>'); //Add the fade layer to bottom of the body tag.
$('#fade').css({'filter' : 'alpha(opacity=0)'}).fadeIn(); //Fade in the fade layer
return false;
});
//Close Popups and Fade Layer
$('a.close, #fade').live('click', function() { //When clicking on the close or fade layer...
$('#fade , .popup_block').fadeOut(function() {
$('#fade, a.close').remove();
}); //fade them both out
return false;
});
});
$.fn.tabs = function () {
return this.each(function () {
var $tabwrapper = $(this);
var $panels = $tabwrapper.find('> div');
var $tabs = $tabwrapper.find('> ul a');
$tabs.click(function () {
$tabs.removeClass('selected');
$(this).addClass('selected');
$panels
.hide() // hide ALL the panels
.filter(this.hash) // filter down to 'this.hash'
.show(); // show only this one
return false;
}).filter(window.location.hash ? '[hash=' + window.location.hash + ']' : ':first').click();
});
};
$(document).ready(function () {
// console.log(window.location.hash);
$('div.tabs').tabs();
});
*) Of course you need to be sure what you're doing. There's always a small risk that it's a malicious script, and you might have not found all evals. #jfriend00's tip on executing the decoding snippets line-by-line is a safer way.
The only way I know of to understand what this code does is to find a safe environment (in case the code has malicious intent) and execute it line-by-line in a debugger and watch what it does as it deobfuscates itself to turn itself into normal javascript. The variable names will often stay obscured, but the giant string in _O will get decoded into something (probably javascript code).
Have a look at: http://www.labnol.org/software/deobfuscate-javascript/19815/
They show you how can you do something like that, it's basically a matter of using chrome debugger to "beautify" the code and make it easier to read.
Some versions of chrome don't have the command on a context menu, just look for the command "Pretty print" (has a icon like -> {})
Once done that, you can use a javascript console to evaluate small snippets of code to reverse engineer it. Eg. the expression (at the beginning of your code)
1) (s\u0065lf + ([] * 0) * 1)
2) '\x5B'
3) ((s\u0065lf + ([] * 0) * 1)[0 ^ 0] == '\x5B')
returns this string on my browser
1) "[object Window]0"
2) "["
3) true
Just find the starting point and follow from there. Obfuscated code follows the same rules as normal one, it's just all messed up.

How do I in vanilla javascript: selectors,events and the need of $(this)

I have 3 pictures cropped by span.main{overflow:hidden}. User can pan the span with touch events and explore the hidden parts of the picture.
Code so far:
document.addEventListener('DOMContentLoaded', function() {
var box = document.querySelector('.main');
box.addEventListener("touchstart", onStart, false);
box.addEventListener("touchmove", onMove, false);
box.addEventListener("touchend", onEnd, false);
});
var startOffsetX, startOffsetY;
var moving = false;
function getPos(ev) {
return {
x: ev.touches ? ev.touches[0].clientX : ev.clientX,
y: ev.touches ? ev.touches[0].clientY : ev.clientY
};
}
function onStart(ev) {
moving = true;
var box = document.querySelector('.main');// I need something like $(this)
var pos = getPos(ev);
startOffsetX = pos.x + box.scrollLeft;
startOffsetY = pos.y + box.scrollTop;
if (ev.preventDefault)
ev.preventDefault();
else
ev.returnValue = false;
}
function onMove(ev) {
if (moving) {
var pos = getPos(ev);
var x = startOffsetX - pos.x;
var y = startOffsetY - pos.y;
var box = document.querySelector('.main'); // I need something like $(this)
box.scrollLeft = x;
box.scrollTop = y;
if (ev.preventDefault)
ev.preventDefault();
else
ev.returnValue = false;
}
}
function onEnd(ev) {
if (moving) {
moving = false;
}
}
The problem is that only the first thumbnail works as expected. I've tried:
-querySelector only returns the first element so if I add ID's and querySelector('#box1,#box2,#box3') should work. Nein. I thing I have a 'this' problem on the functions...
-Place events (as Apple suggests) inline <div class="box" onStart="ontouchstartCallback( ev);" ontouchend="onEnd( ev );"ontouchmove="onMove( ev );" > <img></div> looked like a solution yet...My guess, because of 'this' again...
You want to use the querySelectorAll method instead. It returns all matched elements in the subtree instead of only the first one (which is what querySelector does). Then loop through them using a for loop.
var elements = document.querySelectorAll('.main');
for (var i = 0, ii = elements.length; i < ii; ++i) {
var element = elements[i];
element.ontouchstart = onStart;
// ...
}
The other approach you can take (and it is probably a better one) is to use event delegation and set the event listeners on a parent element and decide which of the pictures is being manipulated by checking the target property of each event.
<div id="pictures">
<span class="main"><img /></span>
<span class="main"><img /></span>
<span class="main"><img /></span>
</div>
var parent = document.getElementById('pictures');
parent.ontouchstart = function (e) {
var box = e.target.parentNode; // parentNode because e.target is an Image
if (box.className !== 'main') return;
onStart(e, box);
};

How to get a list of all elements that resides at the clicked point?

On user click I would like to get a list of all elements that resides at the clicked point.
For example, if user clicks on Hello here:
<div><span>Hello<span></div>
I would like to get the following list:
<span> element
<div> element
<body> element
<html> element
What would be the easiest method to get these elements ?
EDIT: Based on clarification, I think this is what you mean:
EDIT: As pointed out by #Misha, outerWidth() and outerHeight() should be used in lieu of width() and height() in order to get an accurate range.
Also, if there's nothing to prevent event bubbling on the page, then the click should be placed on the document as it will be much more efficient. Even if some other click handler prevents bubbling, you should still have the click on the document, and just handle it separately from those handler that prevent bubbling.
Example: http://jsfiddle.net/57bVR/3/
$(document).click(function(e) {
var clickX = e.pageX
,clickY = e.pageY
,list
,$list
,offset
,range
,$body = $('body').parents().andSelf();
$list = $('body *').filter(function() {
offset = $(this).offset();
range = {
x: [ offset.left,
offset.left + $(this).outerWidth() ],
y: [ offset.top,
offset.top + $(this).outerHeight() ]
};
return (clickX >= range.x[0] && clickX <= range.x[1]) && (clickY >= range.y[0] && clickY <= range.y[1])
});
$list = $list.add($body);
list = $list.map(function() {
return this.nodeName + ' ' + this.className
}).get();
alert(list);
return false;
});​
Original answer:
This will give you an Array of the tag names including the span. Couldn't quite tell if this is what you wanted.
It uses .parents() along with .andSelf() to get the elements, then uses .map() with .get() to create the Array.
Example: http://jsfiddle.net/9cFTG/
var list;
$('span').click(function() {
list = $(this).parents().andSelf().map(function() {
return this.nodeName;
}).get();
alert(list);
});​
If you just wanted the elements, not the tag names, get rid of .map() and .get().
Or if you wanted to join the Array into a String using some sort of separator, just add .join(" ") after .get(), placing your separator inside the quotes.
In the near future this should be possible:
$(document).click(function(e) {
var family = this.elementsFromPoint(e.pageX, e.pageY);
$(family).each( function () {
console.log(child);
});
});
Update 2019
Currently in editors draft:
Elements from point
use parent method to get parent of the current tag recursively or in cycle:
var parentTag = $(this).parent().get(0).tagName;
The jQuery parents() function can do this for you.
To attach a click event to all span tags, for example:
$("span").click(function() {
var parents = "";
$(this).parents().map(function () {
parents = parents + " " + this.tagName;
})
alert(parents);
});
Try something like this
$('span').click(function() {
var parents = $(this).parents();
for(var i = 0; i < parents.length; i++){
console.log($(parents[i]).get(0).tagName)
}
});
check the live demo
http://jsfiddle.net/sdA44/1/
Just javascript implementation:
window.addEventListener('click', function(e){
console.log(document.elementFromPoint(e.pageX, e.pageY))
}, false)
Worked for me in firefox and chrome as of 3-29-2017
Found this:
https://gist.github.com/osartun/4154204
Had to change elements.get(i) to elements[i] to fix it...
I have updated the demo http://jsfiddle.net/57bVR/3/, adding to the code the logic to manage also (if present):
The scrolling of the page
The zoom level html tag (in the project I work it gives to me several troubles)
$(document).click(function (e) {
var htmlZoom = window.getComputedStyle(document.getElementsByTagName('html')[0]).getPropertyValue('zoom');
var clickX = e.pageX,
clickY = e.pageY,
list,
$list,
offset,
range,
$body = $('body').parents().andSelf();
$list = $('body *').filter(function () {
offset = $(this).offset();
marginLeft = offset.left * htmlZoom - $(document).scrollLeft();
marginTop = offset.top * htmlZoom - $(document).scrollTop();
outerWidth = $(this).outerWidth() * htmlZoom;
outerHeight = $(this).outerHeight() * htmlZoom;
range = {
x : [marginLeft,
marginLeft + outerWidth],
y : [marginTop,
marginTop + outerHeight]
};
return (clickX >= range.x[0] && clickX <= range.x[1]) && (clickY >= range.y[0] && clickY <= range.y[1])
});
$list = $list.add($body);
list = $list.map(function () {
return this.nodeName + ' ' + this.className
}).get();
alert(list);
return false;
});

Categories