Just like on facebook, I want to make a sidebar that has the names of friends in which the user can click to open up a corresponding message_block that contains a message_tab to open a flyout with the previous messages and form for new ones.
HTML
<div class = "dock">
<div class = "docking_tabs">
</div>
</div>
I have a problem with .append( ) and being able to manipulate the contents inside. This code works fine on the first try. However when I make a new message tab the previously made tab does not respond. Here is what I have:
JavaScript
$('.chat_list').click(function(){
var user_id = $(this).attr('value');
var el = $('<div class = "message_block">'+
'<div class = "message_tab">'+ '</div>' +
'<div class = "message_flyout">' +
'<div class = "message_content">' +
'<div class = "message_header">'+ '</div>' +
'<div class = "conversation_scroll">' +
'<div class = "scroll_content">' + '</div>' + '</div>' +
'<div class = "message_form">' +
"<form method= 'POST' action= 'http://localhost:8000/newtch' accept-charset= 'UTF-8'>" + "<input name='_token' type='hidden' value='2rS54FJZJZhWPplLmBJSH4CsID7Pgec7iPsDtrnm'>" +
'<div class = "message_form">' +
"<input class='input-mini' placeholder='Type a message' name='status' type='text'>" +
'</div>'+
"</form>" +
'</div>'+
'</div>'+
'</div>'+
'</div>');
$('.docking_tabs').append(el);
$('.message_tab').on('click', function(){
$(this).addClass(user_id);
$(this).siblings('.message_flyout').toggle();
});
$('.message_header').on('click', function(){
console.log('clicked');
$(this).closest('.message_flyout').toggle();
});
});
When you do this:
$('.message_tab').on('click', function(){
$(this).addClass(user_id);
$(this).siblings('.message_flyout').toggle();
});
You're not just adding a click handler to the element you just created, you're also adding another click handler to all previously created elements. Which means the "previous tab" now has two click handlers both executing this:
$(this).siblings('.message_flyout').toggle();
Which would essentially cancel one another out.
Instead of adding a new handler every time you create an element, use event delegation to have a single handler for the whole page:
$(document).on('click', '.message_tab', function(){
$(this).addClass(user_id);
$(this).siblings('.message_flyout').toggle();
});
Execute this once when the page loads (instead of in the '.chat_list' click handler). That way there's just one top-level handler for all matching '.message_tab' elements.
So you end up with something more like this:
$('.chat_list').click(function(){
var user_id = $(this).attr('value');
var el = $('<div class = "message_block">'+
'<div class = "message_tab">'+ '</div>' +
'<div class = "message_flyout">' +
'<div class = "message_content">' +
'<div class = "message_header">'+ '</div>' +
'<div class = "conversation_scroll">' +
'<div class = "scroll_content">' + '</div>' + '</div>' +
'<div class = "message_form">' +
"<form method= 'POST' action= 'http://localhost:8000/newtch' accept-charset= 'UTF-8'>" + "<input name='_token' type='hidden' value='2rS54FJZJZhWPplLmBJSH4CsID7Pgec7iPsDtrnm'>" +
'<div class = "message_form">' +
"<input class='input-mini' placeholder='Type a message' name='status' type='text'>" +
'</div>'+
"</form>" +
'</div>'+
'</div>'+
'</div>'+
'</div>');
$('.docking_tabs').append(el);
});
$(document).on('click', '.message_tab', function(){
$(this).addClass(user_id);
$(this).siblings('.message_flyout').toggle();
});
$(document).on('click', '.message_header', function(){
console.log('clicked');
$(this).closest('.message_flyout').toggle();
})
Hi I did some approach here:
Try to replace:
$('.message_tab').on('click', function(){
$(this).addClass(user_id);
$(this).siblings('.message_flyout').toggle();
});
$('.message_header').on('click', function(){
console.log('clicked');
$(this).closest('.message_flyout').toggle();
})
with this:
el.find('.message_tab').on('click', function(){
$(this).addClass(user_id);
$(this).siblings('.message_flyout').toggle();
});
el.find('.message_header').on('click', function(){
console.log('clicked');
$(this).closest('.message_flyout').toggle();
});
Here an example: https://jsfiddle.net/L3xbyeff/1/. Let me know if this is what you need :)
Good luck!
Related
This question already has answers here:
Jquery each(): variable in callback always has last value?
(3 answers)
Closed 1 year ago.
I have a block of code that reads json data, then constructs 50 divs with contest related information in it. I am working with Gleam.io so people can enter the contest. Each contest is it's own day and has a unique URL.
Currently I have it rendering all my boxes fine, putting in the correct data for each div. What I can't figure out for the life of me is when I click on a box, for it to find and pull the correct URL to put into the modal.
When I do it, the onclick will always just pull the last box's information and display that.
<script>
// FETCHING DATA FROM JSON FILE
$.getJSON("https://cdn.shopify.com/s/files/1/2660/5202/files/data.json?v=1624391152",
function (data) {
var modal = '';
var prizeState = '';
var prizeURL = '';
var card = '';
// ITERATING THROUGH OBJECTS
var cardwrapper = document.getElementById('cardWrapper');
$.each(data, function (key, value) {
var prize = '';
var prizeState = value.prizeState;
prizeURL = value.entryForm;
// DATA FROM JSON OBJECT
var card = document.createElement('div');
card.setAttribute('data-modal', value.prizeDay);
card.classList.add('card');
prize += '<div class="entry-form" onclick="modalPop(' + value.entryForm + ')"><span class="entry-url">' + value.entryForm + '</span></div>' +
' <div class=" ' + value.prizeState + '">' +
'<div class="prizeDay">Day ' + value.prizeDay + '</div>' +
/* '<div class="prizeDate"> ' + value.prizeDate + '</div>' + */
'<div class="prizePhoto"> <img src="' + value.prizePhoto + '" /></div>' +
'<div class="prizeTitle"> ' + value.prizeTitle + '</div>' +
'<div class="prizeWinner">' + value.prizeWinner + ' ' + value.prizeCity + '</div>' +
'<span class="button btn btn-default prizeEnterButton">Enter Contest</span>'
prize += '</div>';
card.innerHTML = prize;
card.addEventListener('click', function(){
modalPop(prizeURL);
console.log(prizeURL, ' from onclick');
});
cardwrapper.appendChild(card);
console.log(prizeURL);
});
});
// Get the modal
var modal = document.getElementById("myModal");
// Get the button that opens the modal
var btn = document.getElementById("myBtn");
function modalPop(prizeURL) {
console.log(prizeURL);
var popupContent = '<h4>' + prizeURL + '</h4>' +
'<span id="modalClose" class="close">×</span>' +
'<span id="widget-code">' +
'<iframe src="' + prizeURL + '" frameBorder="0" allowfullscreen></iframe>' +
'</span>'
$('#myModal #modalReplace').empty().html(popupContent);
$('#myModal').fadeIn(200).addClass('modal-active');
}
// When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
if (event.target == modal) {
$('#myModal').fadeOut(200);
}
}
</script>
The problem with modalPop(prizeURL) is that, when you click the card, this click function should trigger correctly, but it has no idea what prizeURL is. This variable is defined in the function (key, value) function, not in the onclick function. I guess it just logs undefined.
It's just a blind guess, but a neat trick could be to attach each prizeURL to each card, like this :
card.prizeURL = prizeURL;
card.addEventListener('click', function(){
modalPop(this.prizeURL);
console.log(this.prizeURL, ' from onclick');
});
Store the prizeURL as an attribute of the card
...
prizeURL = value.entryForm;
// DATA FROM JSON OBJECT
var card = document.createElement('div');
card.setAttribute('data-prizeurl', prizeURL);
card.setAttribute('data-modal', value.prizeDay);
...
Move your click function out of the loop and delegate it
$(document).on('click', '[data-modal]', function(){
modalPop($(this).data('prizeurl'));
console.log(prizeURL, ' from onclick');
});
// When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
if (event.target == modal) {
$('#myModal').fadeOut(200);
}
}
The issue is that
card.addEventListener('click', function(){
modalPop(prizeURL);
});
needs a closure or a scope change (use let for example)
But this can all be avoided if you delegate and store the URL in a data attribute
const modalPop = prizeURL => {
console.log(prizeURL);
$('#modalReplace').html(`<h4>${prizeURL}</h4>
<span id="modalClose" class="close">×</span>
<span id="widget-code">' +
<iframe src="' + prizeURL + '" frameBorder="0" allowfullscreen></iframe>' +
</span>`)
$('#myModal').fadeIn(200).addClass('modal-active');
}
// When the user clicks anywhere outside of the modal, close it
$(window).on("click", event => {
if ($(event.target).closest("#myModal").length===0 && !$(event.target).hasClass('close')) {
$("#myModal").fadeOut(200);
}
})
$.getJSON("https://cdn.shopify.com/s/files/1/2660/5202/files/data.json?v=1624391152",
function(data) {
$('#cardWrapper').html(
data.map(item => `<div class="card" data-modal="${item.prizeDay}" data-url="${item.entryForm}">
<div class="entry-form" ><span class="entry-url">${item.entryForm}</span></div>
<div class="${item.prizeState}">
<div class="prizeDay">Day ${item.prizeDay}</div>
<div class="prizePhoto"> <img src="${item.prizePhoto}" /></div>
<div class="prizeTitle">${item.prizeTitle}</div>
<div class="prizeWinner">${item.prizeWinner} ${item.prizeCity}</div>
<span class="button btn btn-default prizeEnterButton">Enter Contest</span>
</div>
</div>`))
.on("click", ".prizeEnterButton", function(e) { // clicking enter contest
modalPop($(this).data("url"))
e.stopPropagation();
})
})
#myModal { display: none; position:absolute; top:200px; background-color:white}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="myModal"><h1>MODAL</h1><div id="modalReplace"></div></div>
<div id="cardWrapper"></div>
I have created a custom base template like so:
baseTpl :
'<div class="fancybox-container" role="dialog" tabindex="-1">' +
'<div class="fancybox-bg"></div>' +
'<div class="fancybox-inner">' +
'<div class="fancybox-infobar">' +
'<span data-fancybox-index></span> / <span data-fancybox-count></span>' +
'</div>' +
'<div class="fancybox-toolbar">{{buttons}}</div>' +
'<div class="fancybox-navigation">{{arrows}}</div>' +
'<div class="fancybox-stage"></div>' +
'<div class="fancybox-social">' +
'<button class="sharefb" title="Share FaceBook" data-share="hello"></button>' +
'<button class="sharetwt" title="Share Twitter" data-share="hello"></button>' +
'</div>' +
'</div>' +
'</div>',
How do I target the custom class sharefb? As I want to dynamically change the data-share attribute.
Thank you!
You event needs to be delegated, you will bubble the click event using a parent element, in my case the body:
$('body').on('click','.sharefb',function(){
$(this).attr('data-share','new value');
});
I found out that I can just do like this inside a fancybox instance inside opts:
afterShow : function( instance, current ) {
$('.fancybox-social .sharefb').click(function(){
//add your js here
});
I'm adding livechat to a website, the problem I have is that it adds dynamic onclick to the page which gets blocked by my CSP policy.
I have managed to work around this by removing the dynamic code and adding my own.
What I cannot figure out is how to grab the link id which can either be
id="online-icon" or id="offline-icon" and also the span class lhc-text-status text
Client-side is not my strong point, so apologies if the code below is a mess, but can someone help with adding the above id into my code.
$(document).ready(function () {
$('body').on('click', '.status-icon', function () {
$(this).closest('#lhc_status_container').remove();
$('<div id="lhc_status_container">' +
'<a id="NEED TO ADD STATUS HERE" class="status-icon" href="#">' +
'<span class="lhc-text-status">AND HERE</span>' +
'</a>' +
'</div>').appendTo("#liveChatCase");
$("#online-icon").click(function () {
return window.lh_inst.lh_openchatWindow();
});
});
});
Below is an example of how the dynamic code is added to the page.
<div id="lhc_status_container">
<a id="online-icon" class="status-icon" href="#" onclick="return lh_inst.lh_openchatWindow()">
<span class="lhc-text-status">Live help is online...</span></a>
</div>
UPDATE I now have managed to get the value of id="NEED TO ADD STATUS HERE" but still working on lhc-text-status
Solved problem, the code is below and if anyone can improve on it it would be appreciated as I need to click the live chat div twice before it opens
$(document).ready(function () {
$('body').on('click', '.status-icon', function () {
var statusId = $(this).attr('id');
var textStatus = $(".lhc-text-status").text();
$(this).closest('#lhc_status_container').remove();
$('<div id="lhc_status_container">' +
'<a id="'+ statusId +'" class="status-icon" href="#">' +
'<span class="lhc-text-status">' + textStatus +'</span>' +
'</a>' +
'</div>').appendTo("#liveChatCase");
$(".status-icon").click(function () {
return window.lh_inst.lh_openchatWindow();
});
});
});
OK fixed the double click issue with amended code below
$(document).ready(function () {
$('body').on('click', '.status-icon', function () {
var statusId = $(this).attr('id');
var textStatus = $(".lhc-text-status").text();
// $(this).closest('#lhc_status_container').remove();
$('<div id="lhc_status_container">' +
'<a id="'+ statusId +'" class="status-icon" href="#">' +
'<span class="lhc-text-status">' + textStatus +'</span>' +
'</a>' +
'</div>').appendTo("body");
return window.lh_inst.lh_openchatWindow();
// $(".status-icon").click(function () {
// return window.lh_inst.lh_openchatWindow();
//});
});
});
I have the below code to bind html in my html page,
<div bind-html-compile="menuButtonView"></div>
I have the below code in my controller,
dashboardService.getTemplateMetaData(data.templateCategory)
.success(function(data) {
console.log(data);
$scope.buttonArray = data.btnArray;
$scope.firstMenuLabel = data.firstMenuLabel;
if (data.firstMenuLabel) {
$scope.menuButtonView = '<obl-menu-group label="{{firstMenuLabel}}" icon="fa-pencil-square-o">' +
'<div data-ng-repeat="btn in buttonArray" >' +
'<div ng-if="btn.menuTitle !=="Site Settings" ">' +
'<obl-menu-button label="{{btn.menuTitle}}" icon="fa fa-file-image-o" menu-function="{{btn.menuFunction}}">' +
'</obl-menu-button></div>' +
'<div ng-if="btn.menuTitle ==="Site Settings"">' +
'<obl-menu-group label="{{btn.menuTitle}}" icon="fa-pencil-square-o" class="md-sub-menu">' +
'<obl-menu-button label="Contact Us" icon="fa fa-file-image-o" click-title="contactUs"></obl-menu-button>' +
'</obl-menu-button></div>' +
'</div>' +
'<obl-menu-group>';
}
}).error(function(err) {
});
}).error(function(err) {
});
The ng-if inside the ng-repeat is not working. I can't see anything wrong in this code. Is there a problem with the quotation marks?
use single quote in ng-if
'<div data-ng-repeat="btn in buttonArray" >' +
'<div ng-if="btn.menuTitle !=='Site Settings'">'+
//Your code
</div>'
'<div ng-if="btn.menuTitle ==='Site Settings'">'+
//Your code
</div>'
'</div>'
I set event listener to button and expect new node adding every time the button is pressed, but, in fact, this works only one time. That must be my mistake with ":last" selector which somehow freezes on the same node. How can I fix this?
$("#btnAddWord").on('click', function(){
$('div.input-group:last').after( inputGroup );
});
http://jsfiddle.net/aT82W/
It's because you try to add again and again the same element (instead of a new one). Let's create a new one for each click.
Modified code from your JSFiddle example:
$("#btnAddWord").on('click', function(){
var inputGroup = document.createElement('div');
inputGroup.setAttribute('class','input-group');
inputGroup.innerHTML = '...here your HTML...';
$('div.input-group:last').after( inputGroup );
});
You should use a function that generates new input groups, here you're always referring to the same element (created one time only).
function createInputGroup() {
return $('<div class="input-group">' +
'<span class="input-group-addon">' +
' <input type="checkbox">' +
'</span>' +
'<input type="text" class="form-control" placeholder="New word">' +
'<span class="input-group-addon">' +
' <span class="glyphicon glyphicon-remove-circle"></span>' +
'</span>' +
'</div>');
}
$("#btnAddWord").on('click', function(){
$('div.input-group:last').after( createInputGroup() );
});
Looking at your fiddle link it seems that, inputGroup is no longer available on next click. so try this:
function CreateDiv()
{
var inputGroup = document.createElement('div');
inputGroup.setAttribute('class','input-group');
inputGroup.innerHTML = '<span class="input-group-addon">'+
' <input type="checkbox">'+
'</span>'+
'<input type="text" class="form-control" Placeholder="New word">'+
'<span class="input-group-addon">'+
' <span class="glyphicon glyphicon-remove-circle"></span>'+
'</span>';
return inputGroup;
}
$(document).on("click", "#btnAddWord", function(){
$('div.input-group:last').after( CreateDiv() );
});
$("#btnAddWord").on('click', function(){
var inputGroup = document.createElement('div');
inputGroup.setAttribute('class','input-group');
inputGroup.innerHTML = '<span class="input-group-addon">'+
' <input type="checkbox">'+
'</span>'+
'<input type="text" class="form-control" Placeholder="New word">'+
'<span class="input-group-addon">'+
' <span class="glyphicon glyphicon-remove-circle"></span>'+
'</span>';
$('div.input-group').last().append( inputGroup );
});
you are just moving the same element from place to place (which is the same place by the way).
You need to clone this element by doing
$(inputGroup).clone()