Javascript - ZeroClipboard copied content doesn't get updated - javascript

I have 2 pre blocks, each of them is wrapped with a div and has a copy button.
<div class="code">
<a class="copy">copy</a>
<pre>content of 1st pre</pre>
</div>
<div class="code">
<a class="copy">copy</a>
<pre>content of 2nd pre</pre>
</div>
$('.code').on('mouseenter', function() {
var copy_button = $(this).find('.copy');
var clip = new ZeroClipboard(copy_button, {moviePath: 'ZeroClipboard.swf'});
var content = $(this).find('pre').text();
// at this point, content is always right
// alert(content);
clip.on('mousedown', function(client, args) {
// the content doesn't get updated here
alert(content);
clip.setText(content);
});
});
The problem is, it seems that it always copys the conent of the first-mouseentered-div.
Say I first mouseentered div2, and clicked copy, the content (content of 2nd pre) is copied fine. But then when I try to copy the first pre, the content doesn't get updated, it's still content of 2nd pre.
What am I doing wrong here? How can I fix this?

OK, I found another jQuery plugin - zClip, which is built using the Zero Clipboard library. It's much easier to use and configure.
$('.copy').zclip({
path: 'ZeroClipboard.swf',
copy: function() {
var tocopy = $(this).parent().find('pre').text();
// formatting content
// ...
return tocopy;
},
beforeCopy:function(){
// do something before copy
},
afterCopy:function(){
// do something after copy
}
});

You keep on adding more and more events on mouse enter.
This should unbind events so you don't add each time:
.on('mouseout', function(){
$(this).unbind();
});

Related

How to copy an event listener in plain Javascript

So I am making some overrides on a Wordpress plugin. I need to copy the event listener on an element and then replace the element and add it back. The event listener is generated by the plugin.
I thought getEventListeners() would work but I have read that it only works in console. If that is this case I'm really astounded. We're in freaking 2020 and I am not finding an obvious solution to this.
What is the solution here people?
Below is the code I was trying to implement having assumed getEventListeners wasn't just a console function.
// Edit Affirm
(function replaceAffirm() {
if (document.querySelector(".affirm-modal-trigger")) {
const learnMore = document.querySelector("#learn-more");
const modalTrigger = document.querySelector(".affirm-modal-trigger");
const clickHandler = getEventListeners(modalTrigger).click[0].listener;
const substr = learnMore.innerHTML
.toString()
.substring(
learnMore.innerHTML.indexOf("h") + 2,
learnMore.innerHTML.length
);
learnMore.innerHTML = "Easy Financing with " + substr;
modalTrigger.addEventListener("click", clickHandler);
} else {
setTimeout(function () {
replaceAffirm();
}, 250);
}
})();
HTML
<p id="learn-more" class="affirm-as-low-as" data-amount="20000" data-affirm-color="white" data-learnmore-show="true" data-page-type="product">
Starting at
<span class="affirm-ala-price">$68</span>
/mo with
<span class="__affirm-logo __affirm-logo-white __ligature__affirm_full_logo__ __processed">Affirm</span>.
<a class="affirm-modal-trigger" aria-label="Prequalify Now (opens in modal)" href="javascript:void(0)">Prequalify now</a>
</p>
You can't copy event listeners, but it seems because of the structure of your HTML it's more likely that you shouldn't need to re-add it. Instead of editing the HTML and removing the event listener by doing so, the best bet would be to edit around it.
If you want to remove the text nodes you can iterate through childNodes and separate out what should be removed.
Then to rebuild the appropriate text where you want it you can use insertAdjacentText
if (document.querySelector(".affirm-modal-trigger")) {
const learnMore = document.querySelector("#learn-more");
const modalTrigger = document.querySelector(".affirm-modal-trigger");
const children = Array.from(learnMore.childNodes);
children.forEach(child => {
if (child.nodeType === Node.TEXT_NODE || child.matches(".affirm-ala-price")) {
if (learnMore.contains(child)) {
learnMore.removeChild(child);
}
}
});
learnMore.insertAdjacentText("afterBegin", "Easy Financing With ");
modalTrigger.insertAdjacentText("beforeBegin", " ");
} else {
setTimeout(function() {
replaceAffirm();
}, 250);
}
<p id="learn-more" class="affirm-as-low-as" data-amount="20000" data-affirm-color="white" data-learnmore-show="true" data-page-type="product">
Starting at
<span class="affirm-ala-price">$68</span> /mo with
<span class="__affirm-logo __affirm-logo-white __ligature__affirm_full_logo__ __processed">Affirm</span>.
<a class="affirm-modal-trigger" aria-label="Prequalify Now (opens in modal)" href="javascript:void(0)">Prequalify now</a>
</p>
Yes waiting for the Html element to be loaded and checking until it gets loaded is okay and this is one of the correct ways to wait for it.
As per my understanding of your issue, you just have to change the text of the learn-more element.
for that, it is not necessary to copy event listener and then again binding it.
Instead of replacing the whole element just change the text keeping the same element.
So it gets binded with the event listener by default.

How to add translate="no" via JS to a dynamic translation script?

I have a script on my website that translates the text contained within a span when the user mouses over an anchor, and it works just fine. However, browsers that do auto-translation seem to be grabbing the titles that get generated onmouseover and translating them as well, which defeats the purpose of the script. I have tried adding the property translate="no" to the generated text using JS (as seen below in Line 10) but I must be missing something, because it doesn't seem to be having an effect.
Please help?
JS:
$.each($("li"), function(i, elements) {
var links = elements.getElementsByTagName("a");
var article_title = elements.getElementsByClassName("article-title")[0];
$.each(links, function(j, link) {
var previous_title = article_title.innerHTML;
if (!$(link).is('.newspaper, .doi')) {
link.addEventListener("mouseover", function() {
$(article_title).fadeTo(150, 0.5, function() {
article_title.innerHTML = link.title;
$(article_title).prop('translate', 'no');
$(article_title).fadeTo(150, 1, function() {});
});
});
link.addEventListener("mouseout", function() {
$(article_title).fadeTo(150, 0.5, function() {
article_title.innerHTML = previous_title;
$(article_title).fadeTo(150, 1, function() {});
});
});
}
});
});
});
HTML:
<li>
[EN]
[ES]
<span class="article-title">This is an example</span>
</li>
First, are you sure that the script interprets
translate="no"
Property correctly? As in, it figures that this should not be translated? Maybe the code logic appears to be the culprit.
Second, try using .attr instead of .prop?

Know which user created div was clicked

I apologise if my explanation is hard to understand.
Using This Code:
names.push(newName);
var strName = newName;
var newName = document.createElement('p')
newName.setAttribute('id', newName);
document.body.appendChild(newName);
newName.innerText = strName;
$(newName).css('position','absolute')
$(newName).css('top', y);
$(newName).css('left', x);
updateXY();
The user creates a new div (with a user-inputted name) which works fine.
my problem is that i don't know how to know when one of these user-crated-and-named divs is clicked.
For example:
if the user created 2 divs, 'hello' and 'goodbye' i couldnt just use $('#hello').click(function() {}); etc. Because i wouldnt know that the user would've chose to create the div entitled 'hello'
Furthermore, the array names has all of the names of all of the divs in - if this is any help to anybody. Thankyou, and any help is appreciated
Simply add the event listener to the element as you did when styling it:
$(newName).on("click",function(){
console.log("element "+$(this).text()+" clicked");
});
JSFiddle
Or, with JS's addEventListener():
newName.addEventListener("click",function(){});
JSFiddle
Or, the old way for older browsers, onclick:
newName.onclick = function(){};
JSFiddle
You could do something like this
$(newName).on("click",function(e){
// the variable 'e' is the event of click, if we do e.toElement, we get to know who the element clicked is
var $thisDiv = $(e.toElement);
// do something with $thisDiv
});
After you create a div element, you need to initialize 'click' event on the div
<a class="js-create-div" href="#">Create Div</a>
$(document).ready(function(){
function init(){
var newUserDiv = $(".js-user-div").not(".js-inited");
newUserDiv.on("click",function(){
alert($(this).html());
});
newUserDiv.addClass("js-inited");
}
$(".js-create-div").click(function(){
$("#wrapper").append("<div class='js-user-div'>"+Math.random()+"</div>");
init();
});
});
https://jsfiddle.net/ema0nazs/

Creating a way to navigate back when using jQuery for AJAX

I am dynamically loading content into part of a page using jQuery .load().
It is working well, but I am having trouble building a way for the user to navigate back to the original content after the new content has been loaded.
I have created a 'close' icon with css which exists on the new page which is loaded, but I am not sure how to set up the jQuery / JavaScript in order for it to navigate the user back to the original state of that part of the page.
This is the relevant js:
// pages to load
var loadLudwig = "lw.html";
$("#work a:first-child").click(function() {
$("#work").fadeTo('slow', 0, function() {
$("#work").load(loadLudwig, function(){
$("#work").fadeTo('slow', 1);
});
});
});
// (& this part is working fine)
The relevant HTML (on the original page) is like this (its a grid of images embedded within anchor tags):
<section id="work">
...img and svg stuff
</section>
I tried many variations of:
$("#close-button").click(function() {
$("#work").fadeTo('slow', 0, function () {
$("#work").load('home.html #work', function() {
$("#work").fadeTo('slow', 1);
});
});
});
but this loads the content very strangely / some of the original functionality of #work is lost.
How do I get my close button to navigate back to the original state of #work?
In the jquery documentation for .load() is stated that:
Script Execution
When calling .load() using a URL without a suffixed selector
expression, the content is passed to .html() prior to scripts being
removed. This executes the script blocks before they are discarded. If
.load() is called with a selector expression appended to the URL,
however, the scripts are stripped out prior to the DOM being updated,
and thus are not executed. An example of both cases can be seen below:
Here, any JavaScript loaded into #a as a part of the document will
successfully execute.
1. $( "#a" ).load( "article.html" );
However, in the following case, script blocks in the document being
loaded into #b are stripped out and not executed:
1. $( "#b" ).load( "article.html #target" );
This is a probable cause for lack of functionality.
I'd also look into event binding. In your code examples you're using .click but if you are loading content or you are creating elements on-the-fly you should be favoring .on(). This method delegates events instead of just binding them to a DOM node.
I'd recommend you reading the whole article.
EDIT:
Here is a quick n'dirty way of achieving the effect
// pages to load
var loadLudwig = "lw.html",
$ludwig,
$work = $('#work'),
$workContent = $work.children(),
$closeButton = $("#close-button");
$work.find('a:first-child').click(function() {
$work.fadeTo('slow', 0, function() {
//Here is the tricky part
//Detaching keeps all the jQuery data on the elements
$workContent.detach();
//The first time, load the content,
//if the content is already loaded
//append it to the container
if(!$ludwig){
$work.load(loadLudwig, function(){
//Save the content in a var
//so you can reuse it later
$ludwig = $work.children();
$work.fadeTo('slow', 1);
});
} else {
$ludwig.appendTo($work);
$work.fadeTo('slow', 1);
}
});
});
$closeButton.click(function() {
$work.fadeTo('slow', 0, function () {
//Remove the old content, don't worry
//because is stored in $ludwig
$work.children().detach();
//Instead of reloading the content, just
//attach the fragment again
$workContent.appentTo($work);
$work.fadeTo('slow', 1);
});
});
You probably need to save the html somewhere. For example:
// Top of file
var oldHTML = "";
// Lots of stuff...
$("#work a:first-child").click(function() {
$("#work").fadeTo('slow', 0, function() {
// Store the old html
oldHTML = $("#work").html();
$("#work").load(loadLudwig, function(){
$("#work").fadeTo('slow', 1);
});
});
});
// Code for the close button
$("#close-button").click(function() {
$("#work").fadeTo('slow', 0, function () {
$("#work").html(oldHTML).fadeIn("slow");
});
});
Alternatively, instead of replacing the html, you could create another child. Of course, you might have to slightly change your markup.
<section id="work">
<div id="oldHTML">
...img and svg stuff
</div>
<div id="newSection" style="display:none;">
</div>
</section>
Then replace $("#work") with $("#oldHTML") in your first piece of code like so:
$("#oldHTML a:first-child").click(function() {
$("#oldHTML").fadeTo('slow', 0, function() {
$("#oldHTML").hide();
$("#newSection").load(loadLudwig, function(){
$("#newSection").show().fadeTo('slow', 1);
});
});
});
// Code for the close button
$("#close-button").click(function() {
$("#newSection").fadeTo('slow', 0, function () {
$("#newSection").hide();
$("#work").fadeIn("slow");
});
});

how to repeat same Javascript code over multiple html elements

Note: Changed code so that images and texts are links.
Basically, I have 3 pictures all with the same class, different ID. I have a javascript code which I want to apply to all three pictures, except, the code needs to be SLIGHTLY different depending on the picture. Here is the html:
<div class=column1of4>
<img src="images/actual.jpg" id="first">
<div id="firsttext" class="spanlink"><p>lots of text</p></div>
</div>
<div class=column1of4>
<img src="images/fake.jpg" id="second">
<div id="moretext" class="spanlink"><p>more text</p></div>
</div>
<div class=column1of4>
<img src="images/real.jpg" id="eighth">
<div id="evenmoretext" class="spanlink"><p>even more text</p></div>
</div>
Here is the Javascript for the id="firsttext":
$('#firstextt').hide();
$('#first, #firsttext').hover(function(){
//in
$('#firsttext').show();
},function(){
//out
$('#firsttext').hide();
});
So when a user hovers over #first, #firsttext will appear. Then, I want it so that when a user hovers over #second, #moretext should appear, etc.
I've done programming in Python, I created a sudo code and basically it is this.
text = [#firsttext, #moretext, #evenmoretext]
picture = [#first, #second, #eighth]
for number in range.len(text) //over here, basically find out how many elements are in text
$('text[number]').hide();
$('text[number], picture[number]').hover(function(){
//in
$('text[number]').show();
},function(){
//out
$('text[number]').hide();
});
The syntax is probably way off, but that's just the sudo code. Can anyone help me make the actual Javascript code for it?
try this
$(".column1of4").hover(function(){
$(".spanlink").hide();
$(this).find(".spanlink").show();
});
Why not
$('.spanlink').hide();
$('.column1of4').hover(
function() {
// in
$(this).children('.spanlink').show();
},
function() {
// out
$(this).children('.spanlink').hide();
}
);
It doesn't even need the ids.
You can do it :
$('.column1of4').click(function(){
$(this); // the current object
$(this).children('img'); // img in the current object
});
or a loop :
$('.column1of4').each(function(){
...
});
Dont use Id as $('#id') for multiple events, use a .class or an [attribute] do this.
If you're using jQuery, this is quite easy to accomplish:
$('.column1of4 .spanlink').hide();
$('.column1of4 img').mouseenter(function(e){
e.stopPropagation();
$(this).parent().find('.spanlink').show();
});
$('.column1of4 img').mouseleave(function(e){
e.stopPropagation();
$(this).parent().find('.spanlink').hide();
});
Depending on your markup structure, you could use DOM traversing functions like .filter(), .find(), .next() to get to your selected node.
$(".column1of4").hover(function(){
$(".spanlink").hide();
$(this).find(".spanlink, img").show();
});
So, the way you would do this, given your html would look like:
$('.column1of4').on('mouseenter mouseleave', 'img, .spanlink', function(ev) {
$(ev.delegateTarget).find('.spanlink').toggle(ev.type === 'mouseenter');
}).find('.spanlink').hide();
But building on what you have:
var text = ['#firsttext', '#moretext', '#evenmoretext'];
var picture = ['#first', '#second', '#third'];
This is a traditional loop using a closure (it's better to define the function outside of the loop, but I'm going to leave it there for this):
// You could also do var length = text.length and replace the "3"
for ( var i = 0; i < 3; ++i ) {
// create a closure so that i isn't incremented when the event happens.
(function(i) {
$(text[i]).hide();
$([text[i], picture[i]].join(',')).hover(function() {
$(text[i]).show();
}, function() {
$(text[i]).hide();
});
})(i);
}
And the following is using $.each to iterate over the group.
$.each(text, function(i) {
$(text[i]).hide();
$([text[i], picture[i]].join(', ')).hover(function() {
$(text[i]).show();
}, function() {
$(text[i]).hide();
});
});
Here's a fiddle with all three versions. Just uncomment the one you want to test and give it a go.
I moved the image inside the div and used this code, a working example:
$('.column1of4').each(function(){
$('div', $(this)).each(function(){
$(this).hover(
function(){
//in
$('img', $(this)).show();
},
function(){
//out
$('img', $(this)).hide();
});
});
});
The general idea is 1) use a selector that isn't an ID so I can iterate over several elements without worrying if future elements will be added later 2) locate the div to hide/show based on location relational to $(this) (will only work if you repeat this structure in your markup) 3) move the image tag inside the div (if you don't, then the hover gets a little spazzy because the positioned is changed when the image is shown, therefore affecting whether the cursor is inside the div or not.
EDIT
Updated fiddle for additional requirements (see comments).

Categories

Resources