【问题标题】:Copying an element with all it’s attributes - Update复制具有所有属性的元素 - 更新
【发布时间】:2015-05-21 04:45:27
【问题描述】:

更新 4 嗨@保罗。我想我知道发生了什么,我只是不知道如何解决它。 我在 touchstart 中的警报导致应用程序停止,当我单击 OK 时,touchend 事件已经过去。我删除了 touchstart 警报并且 touchend 警报有效,istouch 值为“true”。 我添加了更多警报以查看失败的位置,发现 math.abs(e.pageX) 不是数字 - 警报显示 NaN。 $snd.data('tx') 也显示为“未定义”。因此,vx 也被报告为 NaN。 ds 的值显示一个数值。

所以,我认为问题在于 $snd 是在 .on(touchstart...) 中定义的,并且不能从 .on(touchend...) 中引用。这可能是变量的范围问题吗?至少复制到搜索结果页面的原始术语和搜索结果显示出相同的症状,因此触发了 onclick 处理程序,这是一个良好的进展。下面是当前版本的点击处理程序,你能告诉我我应该改变什么吗,我对 js 很陌生,这对我来说真的很有挑战性。再次感谢。

$(".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
                    });
                }
            }
        });
    });

更新 3 嗨@Paul,我对您的代码进行了细微更改,我将点击处理程序应用于.wrapper div 而不是Body,因为当我应用您的更新时,主页的链接停止工作,主页链接现在工作正常。 每个英语短语可以翻译成 1,2 或 3 个西班牙语短语,所有西班牙语短语都包含在任何给定英语 div 的单个 .wrapper div 中。

以下示例显示了 3 个英语 div 及其西班牙语翻译。我总共有 800 多个英语短语。当用户搜索这些短语时,应将完整的“英语”div(包括 wrapper div)复制到搜索结果页面,并且看起来复制正确。

<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&iacute;a &eacute;l?</a>
    </div> <!-- End .wrapper -->
</div> <!-- End English -->

更新 2 @Paul,如我在您回复下方的评论中所述,请参阅下面的完整文档就绪脚本以及您的更改以及将所选元素匹配/复制到空 div 的 searchApp 脚本。

这是文档准备脚本:

 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>

这里是搜索/复制脚本

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

更新

我认为我已经本地化了导致我的问题的区域 - 当我将一个元素复制到搜索结果页面中的一个空 div 时,当我点击搜索时不会触发来自原始元素的事件侦听器结果页面。

以下是正在复制的示例英语/西班牙语元素:

<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 -->

下面是在英文文本中找到匹配项时复制原始元素的代码:

if ($(this).text().toLowerCase().match($searchTerm)) {
    $(this).clone(true, true).appendTo("#searchResultsDiv");
}

英语/西班牙语短语正确显示在搜索结果页面上,但不响应点击。我向事件侦听器添加了一条警报消息,当我点击搜索页面时它不会显示,但当我点击原始页面时会显示。

非常感谢您提出的任何建议。谢谢。

结束更新

我有一个智能手机应用程序,它是一个会说话的英语/西班牙语短语手册,当用户点击一个西班牙语短语时,jQuery 会播放一个简短的 mp3 文件,并且工作正常,jQuery 播放器在文档准备就绪时被分配。我现在创建了一个搜索功能,它将所有匹配的英语/西班牙语短语复制到搜索结果页面,这也可以正常工作。

我遇到的问题是,当用户在搜索结果页面上点击任何西班牙语短语时,应用程序会调用系统声音播放器(我认为)而不是 jQuery 声音播放器,系统声音播放器会显示音频控制栏,我不希望我的用户看到。

我已经尝试了所有我能想到的方法,基本上遵循两种方法:

  1. 复制元素的所有属性
  2. 将 onclick 事件分配给搜索结果页面中的父元素

这些方法都不能解决这个问题——至少我所知道的没有解决这个问题。

我对 js/jQuery 还很陌生,所以如果我遗漏了一些非常明显的东西,请原谅我。

请在下面找到:

  1. 一个示例英语/西班牙语元素,有数百个 这些一共。这些将被复制到搜索结果页面,如果 搜索匹配英文短语/单词
  2. 将点击处理程序分配给 西班牙语短语
  3. 查找匹配元素并将其复制到 搜索结果页面

示例英语/西班牙语元素

<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 -->

文档准备脚本

这还可以做一些事情,比如改变背景颜色、检查点击与滑动等。

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

搜索功能

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

更新 5 我将警报更改为 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 

第二个测试是点击一个创建的西班牙元素。这是日志

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 

它们都没有发出声音。下面是 touchstart 和 touchend 处理程序脚本,显示了我添加 console.log 语句的位置。

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

【问题讨论】:

  • 建议:将您的问题缩减为仅相关的问题。为了帮助您,目前需要阅读很多内容。
  • 你说得对,有很多东西要读,但我不确定问题出在哪里。我已经把它剪掉了 - 抱歉
  • 请不要说早安/晚上有问题!它会被遍布全球的用户在您发布后随时阅读。
  • 我已经为我的主页调整了script-tutorials.com/html5-audio-player-with-playlist,它可以在没有移动设备声音控制​​条的情况下播放 mp3……至少在我的关系上。不知道它是否与 ios 设备相同......也许这会对你有所帮助
  • @errand 谢谢,我看了看,但我不确定这会解决我的问题,我认为这是点击处理程序,在 doc 中定义了 1,000 多个西班牙语短语,得到“断开连接”当我将西班牙语短语复制到搜索结果页面时。

标签: javascript jquery event-delegation


【解决方案1】:

您的代码中的主要问题是您将事件处理程序添加到一组对象,而不是将它们重新添加到克隆对象。活动委托会更适合你。将您的 ${'.spanish') eventHandlers 更改为:

$("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
                    });
                }
            }
        });

基本上,我只是将您的函数拆分为两个单独的 jquery 事件委托函数。所有这些处理程序都将附加到 body 容器中与选择器 .spanish 匹配的所有内容。您可以对其进行修改以进行一些优化(如果您有一些 div 作为所有这些元素的包装器)。

jQuery $.on 函数可以将一些事件(touchstarttouchendclick in this case)绑定到当前现有元素,以及将来将创建的元素,例如在克隆元素的过程中 -这可能是最适合您的解决方案。

在此处了解更多信息:http://api.jquery.com/on/

【讨论】:

  • 您好@Paul,感谢您提供代码和解释以及指向 .on() 信息的链接。我尝试了您提供的代码,但不幸的是它在搜索结果页面中不起作用,现有的西班牙语条目也不起作用,它们以前起作用。我浏览了你的代码,没有看到任何错误。如果您能够再看一眼,我已经对我的原始问题添加了另一个更新,显示了完整的文档就绪脚本和复制所选英语/西班牙语短语的脚本。非常感谢您的宝贵时间。
  • 当我说“没用”时,我应该说他们没有播放短声音文件,也没有显示我添加的警报消息。
  • @TonyBabb - 向我们展示更多您的 HTML - 也许这就是问题所在。
  • 看起来 touchend 没有被检测到,我添加了警报消息并显示了 touchstart 警报,但没有显示 touchend。这是 touchstart alert.on("touchstart", ".spanish", function(e) { if (istouch) { alert("touchstart detected");
  • 这是触摸端警报
猜你喜欢
  • 2019-02-26
  • 1970-01-01
  • 1970-01-01
  • 2015-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-19
  • 1970-01-01
相关资源
最近更新 更多