Dynamically change Tweet Button "data-text" contents - javascript

This question comes very closely to what I'm after: Replace an Attribute in the Tweet Button with Jquery
However, the suggested solution works just once. That is, I cannot use it in my switch statement like this:
switch(element.id)
{
case "t1":
$(document).ready(function(){
$('a[data-text]').each(function(){
$(this).attr('data-text', Text_Variant_1);
});
$.getScript('http://platform.twitter.com/widgets.js');
});
break;
case "t2":
$(document).ready(function(){
$('a[data-text]').each(function(){
$(this).attr('data-text', Text_Variant_2);
});
$.getScript('http://platform.twitter.com/widgets.js');
});
...
}
What happens is that the data-text attribute is set according to whichever case happens first and doesn't change afterwards.
How can I change data-text attribute of a Tweet Button as many times as I need?
Update: here's the page I'm working on: http://zhilkin.com/socio/en/
The Traits table can be safely ignored. What I want to do with the Sociotypes table is that when you click on a type, the data-text of the Tweet Button below the description on the right should be changed accordingly.
Right now it works like this: if I hover on or click "Don Quixote", then data-text is set to "... Don Quixote ...", and it stays the same if I click "Dumas" later. And vice versa: if I hover on or click "Dumas", then data-text is set to "... Dumas ..." and doesn't change if I click "Don Quixote". (Other types are empty at the moment.)
So, the Tweet Button is only changed the first time I run the script, but I need it to be updated as many times as the type changes.

I struggled with this for a couple of hours this morning, but finally got it working! The problem is essentially that you can only include the twitter widgets.js script once in the page, and that script evaluates the data-text attribute on load. Therefore, in your example, you dynamically set the data-text attribute before loading the script, which will work as expected. However, you can then make no further updates as the script has already run.
I saw this article suggesting you can call twttr.widgets.load() again at runtime to re-evaluate and re-render the buttons, however that didn't work for me. This is because that function re-evaluates <a> tags, not <iframe> tags!
So the solution, as pointed out here, is to completely remove the rendered <iframe> from the DOM, then make a new <a> element with all the appropriate attributes before calling twttr.widgets.load() to finally re-evaluate it and turn it into an <iframe>.
Please see this fiddle for a working example!

You can also use Twitter's createShareButton API call:
function callAsRequired(){
var nodeID = 'YourTwitterNodeID'
//Remove existing share button, if it exists.
var myNode = document.getElementById(nodeID);
while (myNode.firstChild) {
myNode.removeChild(myNode.firstChild);
}
//Create button and customise
twttr.widgets.createShareButton(
'http://your.custom.url.here/',
document.getElementById(nodeID),
{
count: 'none',
text: 'Your custom tweet here'
});
}

since you are using each loop you can use if statements instead:
$(document).ready(function(){
$('a[data-text]').each(function(){
if (theCase == 1) {
$(this).attr('data-text', Text_Variant_1);
}
else if (theCase == 2) { ... }
})
});

Related

Manually force trigger a change event on select in nested form using stimulusjs

Using Haml, ruby 2.7.2, rails 6.1.2.1, stimulus ^2.0.0
So, I am using a nested form that shows a select type. Based on the value of the select type, I will either show or hide a div. I'm converting my coffeescript to stimulus, and I'm not sure how to force trigger a change event, or instead just run the code that checks the value of the select to decide if I should hide or show the div. In the past, Inside my html.haml view I would call this:
:javascript
$('select.answer_type').change()
Based on another post in here: https://discuss.hotwire.dev/t/triggering-turbo-frame-with-js/1622, I tried to change it to say:
:javascript
q_typeTarget.dispatchEvent(new Event('change'));
And that didn't work. Maybe there is another way to do this, or I'm using the wrong vocabulary to look up the example I want to do? I'd rather just manually execute the Controller#action and give it the select object to work off of right away, but I'm not sure how to do this in html/script tag. Thanks for any help!
Here is my relevant code that should help:
_question_fields.html.haml
.nested-fields.question
.form_group.grid-x
.fields.cell.shrink
= f.select :answer_type,
options_for_select(["String","Option","Checkbox"],
f.object.answer_type),{},
class: 'answer_type',
data: {nested_form_target: 'q_type',
action: 'nested-form#update_q_type'}
.cell.answers_group{ style: 'visibility: hidden; display: none'}
= render partial: 'answers', locals: { f: f, q_level: q_level }
:javascript
q_typeTarget.dispatchEvent(new Event('change'));
nested_form_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
static targets = ["q_type", "answers"]
update_q_type(event) {
event.preventDefault()
console.log('You have selected ' + this.q_type )
let d_question = event.target.closest(".question")
let d_select = d_question.querySelector("select")
let d_answer = d_question.querySelector(".answers_group")
console.log('Select: ' + d_select.value )
switch(d_select.value) {
case 'Option':
case 'Checkbox':
console.log(' Show Answers ')
d_answer.style.visibility = "visible"
d_answer.style.display = null
return
default:
console.log(' Hide Answers ')
d_answer.style.visibility = 'hidden'
d_answer.style.display = 'none'
return
}
}
Here is an example .gif of the nested forms, shown below:
I add a Question
I select an option from the select tag, which triggers the select → change event: nested-form#update_q_type
If I select either Option or Checkbox, then I show the div, otherwise I hide the div
Now, here is why I'm trying to trigger the select → change event. If I start to edit this record again, it loads the data just fine. However, it does not know when to show the answers div or not. That's why I need to trigger the select → change event so that it can properly set up the answers div to either hide or show. Otherwise, it's defaulted to always 'hide'.
I have another .gif below. As you can see, it loads the select with Option selected. if I try to select Option again, it doesn't do anything...because it hasn't really changed. If I change it to Checkbox, it of course updates. So basically, I need to do this onLoad, you can say.
Maybe there is a different way to do this? Thank you for your help!
Update: I didn't realize my html tags were being interpreted as html. Oops. Changed all tags to italicized words. Ex: '/</select/>/' is now just select. Also I expanded on the question for more clarification.
StimulusJS has an initialize function that essentially runs when the page loads. You would use that for this purpose. The only other issue here is that you're passing in an event to update_q_type and you won't have an event on initialize. So what I find works is to extract most of the code to a separate function that my initialize and click function can both call. This helps you keep your code DRY.
export default class extends Controller {
static targets = ["q_type", "answers"]
initialize() {
this.switch(this.q_typeTarget)
}
update_q_type(event) {
event.preventDefault
this.switch(this.q_typeTarget)
}
switch(q_type) {
...
}
}
One thing to note, it's not clear if you have one stimulus controller running here or one for every select box combo. You could do the 2nd and not have to use the closest method. It's meant to be very modular.

Change multiple div contents on another div mouse hover without css/with an xml source file

I'm working on this site, and I need to change the contents of image_preview, title_preview, description_preview, link_preview according to what I'm hovering over (ex: mouse hover "button_a" = image1.png, iliketitle, ulikedesc, welikelink).
I've tried using css solutions like this and this, but I wasn't able to make them work like I needed.
Since the page will have many button_#'s (50-100 buttons), I think css isn't a proper choice.
So what I'm looking for is a way to do this without css, better if with an xml source file, so it'd be easier to manage the content to display for each button. I only found this talking about the xml I'd need, but I'm not sure that's exactly what I need.
Your buttons have a class (e.g. .btn) and the associated data to each button is store somewhere, let's say each button has a data-* attribute which points to the right data.
$('.btn').hover(function() {
var data = $(this).data('something');
if(data == "b1") {
//assign the values related to b1
}
else if(data == "b2") {
//assign the values related to b2
}
//and so on
}
If you have a lot of buttons like that, then the data can be a reference to an array containing the proper info.
Here's a jsfiddle DEMO.
And here's updated DEMO.
EDIT:
.hover() can take two handler which the second will handle when mouse is out of the element.
yourElement.hover(
function() {
//mouse is on the element, do stuff
},
function() {
//mouse is out, do other stuff
}
);
You can have a function to set the default values and call that in hover's second function.
jsfiddle DEMO

Reverting to original element ID

I have li blocks which onclick will change class ID as follows:
onclick = "document.getElementById('procblock1').id = 'procblock1Clicked';"
"document.getElementById('procblock2Clicked').id = 'procblock2';"
"document.getElementById('procblock3Clicked').id = 'procblock3';"
"document.getElementById('procblock4Clicked').id = 'procblock4';"
The line document.getElementById('procblock2Clicked').id = 'procblock2'; should revert any clicked elements (blocks) back to their original ID names.
The code works for changing the original id to the clicked id but doesn't have any effect in reverting previously clicked to the original as per lines 2,3 & 4.
I have searched hard for similar questions but can find nothing that covers this specific issue.
#Matthias - I acted upon your advice and came up with a very simplified jquery solution :
`$(function() {
$(".showinfo").click(function() { //using class instead of ID
$(".showinfo").removeClass("clicked"); //Remove all existing clicks
$(this).addClass("clicked"); //add the class to the clicked element });
});`
Posted solution in case anyone else has same query. Your help was appreciated.
If this really is part of the code you're using lines 2-4 won't work; onclick will only handle the first line. You should wrap it as a function to be called onclick, like onclick = "doStuff()" and add a function doStuff () {/* your code here */}. But that's just guessing as you only provide some part of the code in question.
In addition would be good to know what you want to achieve - in case you want to mark clicked elements, it would be a cleaner approach to add a class, e.g. "clicked" or "active", that you simply remove later on instead of changing ids.

Read more opens 1st one all the time

I've a page with about 10 short articles.
Each of them as a "Read More" button which when pressed displays hidden text
The issues I have at the moment is when I press the "Read More" on any of the 10 button it shows the 1st articles hidden content and not the selected one.
I think I need to set a unique ID to each article.. and the read more button be linked to it.. But I don't know how to set it.
I looked at this but couldn't get it working how to give a div tag a unique id using javascript
var WidgetContentHideDisplay = {
init:function() {
if ($('#content-display-hide').size() == 0) return;
$('.triggerable').click(function(e){
var element_id = $(this).attr('rel');
var element = $('#'+element_id);
element.toggle();
if (element.is(':visible')) {
$('.readmore').hide();
} else {
$('.readmore').show();
}
return false;
});
}
}
var div = documentElemnt("div");
div.id = "div_" + new Date().gettime().toString;
$(document).ready(function(){ WidgetContentHideDisplay.init(); });
OP Edit: Sorry, the original code wasn't in caps. I kept getting errors when trying to post, so I copied the code into Dreamweaver and it made it all caps for some reason.
Instead of selecting the element to toggle with an ID (i.e. $('#'+ELEMENT_ID)) you could setup a class for your item and use the class selection (e.g. $('.DETAILED-ARTICLE)') to select the child (or the brother, etc. depending how you built the HTML page).
In theory each ID should point to a single element but each class can be put to as many elements as you want.
If you're getting errors, read the errors and see what they are. Off of a quick read of your code, here are a couple things I noticed that will probably cause issues:
"documentElemnt" is misspelled, which will render it useless. Also, documentElement is a read-only property, not a function like you're using it.
toString is a function, not a property, without the parentheses (.toString()) it isn't going to function like you want it to.
Run the code, look at the errors in the console, and fix them. That's where you start.

How to add jquery events to added classes?

I am trying to create an image gallery, I am using numbers instead of left/right arrows. At the moment, I am trying to get it working with only 2 image (i.g 2 numbers)
this is the html . the id page, is the highlighted number
<div class="grid_1 pagelink" id="page"><p>1</p></div>
<div class="grid_1 pagelink"><p>2</p></div>
the first time the page loads, the code below works. so when I click on link 2 the the code bellow runs fine. But then I want the same code to be triggered when I click back on the first link; but when I do that, the page refreshes by ignoring the code bellow:
$('.pagelink').live('click', function(e){
e.preventDefault();
var frame = $(this).text() - 1;
var frames = 240 * frame;
// $('#gal').animate({marginLeft:'500px'},'slow');
$('#gal').animate({marginLeft: "+="+frames+'px'},'slow');
$(this).attr('id', 'page').removeClass('pagelink');
$('#page').addClass('pagelink').removeAttr('id','page');
// $('#book').animate({ left: '50' });
})
I thought that the .live() would do that for me but it is not working.
I hope you could help
thank you
Previous, this is because you are removing the class of link "pagelink" which were used to map the clicked event.
Also, use another class instead of id(#page) to identify the #page link, id might be problem if its already assign to other link. like
$(this).removeClass('pagelink').addClass('page');
$('.page').addClass('pagelink').removeClass('page');
live should work fine. i think you have a bug in your code, and its probably here:
$(this).attr('id', 'page').removeClass('pagelink');
$('#page').addClass('pagelink').removeAttr('id','page');
what exactly are you trying to accomplish with this code?
when you click on page2, the you set the div's id to be page, but now you have 2 elements with an id of page, and when you select that id, you get the first one (ie page1), but you still remove the class pagelink from page2
in other words, the bug is that at some point you will have 2 elements with the same id (and ids must be unique btw) so when you select that id with $('#page') you always get the first one

Categories