I have the following code which will hide the parent div of the link you click on. When the div's are relatively positioned, there is no problem, but when they are absolutely positioned, they won't remove.
The script also saves the state of each element to localStorage, and the error only happens the first time you try to close one of the divs. What I mean by this is that, if you show three divs, say div one, two, and three, and then try to close the top most div, it won't close. If you reload the page, and try to close div three, which is on top from before you reloaded the page, it will close.
There's a little bit too much code to post, so here's the jsFiddle for it. But here's the code for posterity:
function loadWidgets() {
$(".base").each(function () {
var id = $(this).attr("id");
var counter = localStorage.getItem("counter-" + id) || 0;
var active = localStorage.getItem(id + "-active") || "";
$.each(active.split(" "), function (k, v) {
var s = v.split(",");
if (s.length != 2) {
return;
}
var newElement = $("#" + s[0]).clone(true, true);
newElement.attr("id", s[1]).attr("class", "drag " + id).data("id", id).appendTo("body");
});
});
}
function closeWidget() {
var id = $(this).parent().attr("id").match(/[a-zA-Z]+/g);
$(this).parent().remove();
var active = [];
$($("." + id).not(".base")).each(function () {
active.push(id + "," + $(this).attr("id"));
});
active = active.join(" ");
localStorage.setItem(id + "-active", active);
}
function cloneWidget() {
var id = $(this).attr("href").match(/[a-zA-Z]+/g);
var key = "counter-" + id;
var counter = localStorage.getItem(key) || 0;
counter++;
var newElement = $("#" + id).clone(true, true);
newElement.attr("id", id + counter).attr("class", "drag " + id).appendTo("body");
var active = [];
$($("." + id).not(".base")).each(function () {
active.push(id + ',' + $(this).attr("id"));
});
active = active.join(" ");
localStorage.setItem(id + "-active", active);
localStorage.setItem(key, counter);
}
loadWidgets();
$(".nav a").click(cloneWidget);
$(".close").click(closeWidget);
And the HTML:
<div class="base" id="one" style="background-color:blue">
<a class="close" href="#">Close</a>
<input class="input" id="test"/>
<textarea class="textarea" id="test2"></textarea>
</div>
<div class="base" id="two" style="background-color:red">
<a class="close" href="#">Close</a>
</div>
<div class="base" id="three" style="background-color:green">
<a class="close" href="#">Close</a>
</div>
<div class="nav">
One
Two
three
</div>
jQuery's clone doesn't copy events by default:
.clone( [withDataAndEvents] [, deepWithDataAndEvents] )
[...]
Normally, any event handlers bound to the original element are not copied to the clone. The optional withDataAndEvents parameter allows us to change this behavior, and to instead make copies of all of the event handlers as well, bound to the new copy of the element.
And since you have events bound to things inside what you're cloning, you want deepWithDataAndEvents to be true as well:
var newElement = $("#" + s[0]).clone(true, true);
Corrected fiddle: http://jsfiddle.net/ambiguous/Jdutt/
You create a new widget but you do not assign an event handler to the Close link.
Take the next line and put it at the end of the cloneWidget function:
$("#" + id + counter + " a.close").click(closeWidget);
Related
What i want it to do is add an additional div every time the add button is clicked. what happens instead is it adds it once and then never runs through the code again. I have tried printing out the count and it increments once and never populates again. If i delete the code about inserting the div the count increments every time the button is clicked. What am i missing here?
var count = 0;
button.onclick = function()
{
popup.document.body.innerHTML += '<div id="queryPart-'+ count+'"></div>';
count = count + 1;
};
here is the whole code block
var popup = open("", "Popup", "width=600,height=200,top=600,left=200");
//var div = popup.document.createElement("DIV");
//div.id = "div";
popup.document.body.innerHTML += '<div id="queryPart-0"></div>';
var div = popup.document.getElementById("queryPart-0");
queryLine(div);
var button = popup.document.createElement("BUTTON");
var t = popup.document.createTextNode("ADD");
button.id = "addbutton";
button.onclick = function()
{
popup.document.body.innerHTML += '<div id="queryPart-'+ count+'"></div>';
count= count + 1;
};
button.appendChild(t);
div.appendChild(button);
You should use Node.appendChild() instead because DOM manipulation is a better approach. You only want to add to the existing document, without obstructing or disturbing other elements. By appending directly to the HTML of the body, you are disassociating the original button with the click handler.
Googling this issue returns many helpful resources:
https://www.google.com/#q=innerhtml+removes+listener
After a bit on redirecting, I found one of the first questions related to this issue, here on Stack Overflow:
Is it possible to append to innerHTML without destroying descendants' onclick functions?
Broken
var button = document.getElementById('addBtn');
var count = 0;
button.onclick = function() {
console.log('click');
document.body.innerHTML += '<div class="queryPart-'+ count+'">' + count + '</div>';
count++;
};
<input type="button" id="addBtn" value="Press Me" />
Corrected
var button = document.getElementById('addBtn');
var count = 0;
button.onclick = function() {
var queryDiv = document.createElement('DIV');
queryDiv.className = 'queryPart-' + count;
queryDiv.innerHTML = count;
document.body.appendChild(queryDiv);
count++;
};
<input type="button" id="addBtn" value="Press Me" />
You could also...
set the outerHTML of a div, so that you don't have to create the Element as you see in the above example.
var button = document.getElementById('addBtn');
var count = 0;
button.onclick = function() {
var queryDiv = document.createElement('DIV');
document.body.appendChild(queryDiv);
queryDiv.outerHTML = '<div class="queryPart-'+ count+'">' + count + '</div>';
count++;
};
<input type="button" id="addBtn" value="Press Me" />
This is happening because of the way innerHTML works.
(get) .innerHTML -> "Enumerate all of the elements in this DOM into "HTML" format, and return it as a string"
(set) .innerHTML = -> "Destroy all elements in this DOM, and then create new ones using the HTML that has been provided."
You're destroying your button, and its event listener, during the set operation. You'll likely want to create your button using a more manual method, like document.createElement, and then append it using myElement.appendChild. These operations are pretty common, so I don't think it would make sense to give a full tutorial here - this could also be easier with a framework like JQuery, which has many methods for adding new DOM elements.
Looks like you messed up the order of the
" and '
try:
var count = 0;
button.onclick = function()
{
popup.document.body.innerHTML += '<div id="queryPart-'+ count + '"></div>';
count = count + 1;
};
I have HTML structures in a form system where a html node will have data-src="formname" and this will contain lots of html nodes with data-fld="fieldname". This would be easy to parse but sometimes a field can be a host of a subform that itself contains data-src and data-fld.
When I search for [data-src='name'] using jquery find selectors I get both the immediate data-fld elements and the ones contained in a child data-src, I only want the former, not the latter.
I've created a fiddle to demonstrate:
<div data-src="mainform">
<div data-fld="field1">fld1</div>
<div data-fld="field2">
<div data-src="subform">
<div data-fld="subfield1">subfld1</div>
</div>
</div>
</div>
<div id="info"></div>
And the JS:
var result = "";
var find = "mainform";
var src = $("[data-src='" + find + "']");
src.find("[data-fld]").each(function() {
var ele = $(this);
if (ele.closest("[data-src='" + find + "']") === src) {
result += "Child field : " + $(this).data("fld") + " ";
}
});
$("#info").text(result);
The above code works, by virtue of that IF statement, I think it would be nice to be able to select "[data-fld]" where its closest "[data-src]" is the one I'm working on, and I wondered if (a) there's an inherent JQuery/CSS selector way of doing this or (b) is there otherwise a better solution to this code.
Not only because we want elegant code but also because asking for closest on every loop iteration is going to be a performance issue, possibly.
using immediate children selector
var result = "";
var find = "mainform";
var src = $("[data-src='" + find + "']");
src.find("[data-fld]").first().each(function() {
var ele = $(this);
result += "Child field : " + $(this).data("fld") + " ";
});
$("#info").text(result);
I have a setup where i am trying to assign a mouseover and mouseout event on a div, but they dont appear to be firing. Right now i just have it trying to console.logthe mouseout event and add classes to the body as well for the mouseover event.
I have trying using .addEventListener('mouseover', function(){}) instead of .onmouseover to no avail. I have also tried mouseenter and mouseleave but these are IE only events.
I need to use pure Javascript rather than any 3rd party library, this also needs to work in IE8+.
HTML
<div class="of_expandable" data-channel="2935725596001" data-muted="1" data-skin="dark"></div>
JS
/*
for each expandable element this function:
- parses the data attributes for handing to the embeds
- styles the expandable div
- binds the hover event to the div
*/
function processExpandable (elm, index){
elm.className += " processed";
elm.id = exp_ns + "_expandable_" + index;
// option parsing
var options = {};
options.skin = elm.getAttribute("data-skin");
if(!options.skin){
options.skin = 'light';
}
// styling
elm.style.width = "300px";
elm.style.height = "250px";
elm.style.position = "relative";
elm.style.backgroundColor = "#000";
// add events to elm
elm.onmouseover = function (){
launchModal(elm, options.skin);
};
elm.onmouseout = function (){
console.log('mouseout');
};
}
/*
opens the modal and populates
*/
function launchModal (elm, skin){
console.log('entered');
document.body.className += " " + exp_ns + "_modal_open modal_skin_" + skin;
}
/*
closes the modal and clears
*/
function closeModal (){
// TODO: clear data from modal
var pattern = "/\b" + exp_ns + "_modal_open\b/";
document.body.className = document.body.className.replace(pattern,'');
var pattern = "/\bmodal_skin_light\b/";
document.body.className = document.body.className.replace(pattern,'');
var pattern = "/\bmodal_skin_dark\b/";
document.body.className = document.body.className.replace(pattern,'');
}
/*
adds the modal element waiting to be triggered by the expandables
- adds background
- adds modal box
*/
function addModal (){
var modalHTML = '<div id="' + exp_ns + '_modal" class="exp_modal"></div><div id="' + exp_ns + '_modal_back" class="exp_modal_back"></div>';
document.body.innerHTML += modalHTML;
}
/*
tests if an element has a class
*/
function hasClass(elm, cls) {
return (' ' + elm.className + ' ').indexOf(' ' + cls + ' ') > -1;
}
var exp_ns = "of"
, expandables = getElementsByClassName(exp_ns + '_expandable')
, hoverTimer
, hoverPause = 1000;
if(expandables.length > 0){
for (var i = 0; i < expandables.length; i++){
if(!hasClass(expandables[i], "processed")){
// make sure we arent adding twice from a double include
processExpandable(expandables[i], i);
}
}
// make sure we arent adding twice from a double include
var modal = document.getElementById(exp_ns + '_overlay');
if(!modal){
addModal();
}
}else{
console.log('warning: no expandable elements found');
}
here's a JSFiddle
SOLUTION UPDATE:
So it appears that the reason this was breaking was because of the way that I was inserting the modal elements using document.body.innerHTML += . Which I think must read all the innerHTML with the newly appended content. As a better solution I used this:
function addModal (){
var modalBack = document.createElement("div");
modalBack.setAttribute("class", "exp_modal_back");
document.getElementsByTagName("body")[0].appendChild(modalBack);
var modalCont = document.createElement("div");
modalCont.setAttribute("class", "exp_modal");
document.getElementsByTagName("body")[0].appendChild(modalCont);
}
updated JSFiddle
Your problem is not with how you are using the event handlers. The problem is being caused by the "addModal" function called after the "processExpandable" function. I don't understand what you are trying to accomplish so I can't help you there, but it is a start.
Also, I think you have a problem in the "launchModal" function. Do you really want to keep adding and adding values to the class attribute of the body?
You can try this way :
<div style=" height: 200px; width:200px; background-color: black;"
onmouseover="displayElement('myDiv');"
onmouseout="hideElement('myDiv');">
<div id="myDiv" class="of_expandable" data-channel="2935725596001" data-muted="1" data-skin="dark" style="width:100%; height:100%;display: none; background-color:green;">Content</div>
</div>
here is a JSBin to show you
The goal is to have info from a link in #list and have it create a new link in #list3.
Here is the link http://jsfiddle.net/4y5V6/24/
Is there a way to make it where once you click a link it will push it to #list3 and make the link look like this twitch.tv/chat/embed?channel=(channelname)&popout_chat=true ?
Thank you for your time!!
HTML:
<div id="navi">
<ul>
<li>Stream Selection:</li>
<li>SC2<ul id="list"><li><ul></ul></li></ul></li>
<li>League<ul id="list2"><li><ul></ul></li></ul></li>
<li>Chat<ul id="list3"><li><ul></ul></li></ul></li>
</ul>
</div>
<iframe id="iframe1" name="iframe1" type="text/html" src="" frameBorder="0" height="200px" width="300px">
</iframe>
Current function that populates the list.
// first set of streams SC2
$.getJSON("https://api.twitch.tv/kraken/search/streams?q=starcraft&limit=18&&type=top&callback=?", function (data) {
var temp = "";
$.each(data.streams, function (index, item) {
temp = temp + "<li><a target='iframe1' href='http://www.twitch.tv/widgets/live_embed_player.swf?channel=" + item.channel.name + "'>" + item.channel.display_name + "</a></li>";
});
$("#list ul ").html(temp);
});
// next set of streams LoL
$.getJSON("https://api.twitch.tv/kraken/search/streams?q=League%20of%20Legends&limit=18&&type=top&callback=?", function (data) {
var temp = "";
$.each(data.streams, function (index, item) {
temp = temp + "<li><a target='iframe1' href='http://www.twitch.tv/widgets/live_embed_player.swf?channel=" + item.channel.name + "'>" + item.channel.display_name + "</a></li>";
});
$("#list2 ul ").html(temp);
});
$('#navi').on('click', '#list a', function(e) {
var x = $(this).prop('href'),
y = x.slice(x.indexOf('channel')+8),
z = 'twitch.tv/chat/embed?channel=('+y+')&popout_chat=true',
clone = $(this).clone().attr('href', z);
$('#list3 ul').append($('<li />', { html: clone }));
});
Fork
Alternate version: No duplicates.
Adding ability to open new chat link in new smaller window
jsFiddle only copies list 1 to list 3
jsFiddle Copies from both list 1 and 2 to list 3
// The following call is jQuery's way of assigning events to both "Static"
// && "Dynamic" Elements. This way, even if the element is loaded after
// page load, it still gets the event callback.
// Keep in mind, you do need either a static parent (such as below, I use
// `$('#navi')`, since I assume this element is always present on your page
// load) |OR| you can use `$(document)`. The later is often recommended
// against, as you could run into "slowed down response" issues or naming
// issues. Personally, I use `$(document)` all the time and haven't had a
// complaint yet.
// Then you simply assign your event(s) to your "Selectors"
// For just list 1 that would be
// .on('click', '#list a'
// For list 1, and 2 that would be
// .on('click', '#list a, #list2 a'
// See Also: http://api.jquery.com/on/
$('#navi').on('click', '#list a, #list2 a', function(e) {
var x = $(this).prop('href'), // get href of <a> clicked
y = x.slice(x.indexOf('channel')+8), // extract name
z = 'twitch.tv/chat/embed?channel=('+y+')&popout_chat=true', // create new href link
clone = $(this).clone().attr('href', z), // clone this <a> and replace href with new one created
item = $('<li />', { class: y, html: clone }); // create new <li> making our cloned <a> it's innerHTML
if (!$('#list3 ul .' + y).length) $('#list3 ul').append(item); // check <li> item doesn't already exist and add to list
}) // Thanks to jQuery "chaining", I don't have to "recall" our static parent
.on('click', '#list3 a', function(e) {
e.preventDefault();
var lnk = $(this).attr('href');
// The following is basic JavaScript and simply opens a new window
// simply adjust width and height based on PIXELS
// see more window.open options #: http://www.w3schools.com/jsref/met_win_open.asp
// See Also: http://stackoverflow.com/questions/tagged/javascript+window+open
window.open(lnk, "myWindowName", "width=800, height=600");
})
I am in the process of learning JS, and am trying to add an integer (number of clicks) to all links on my page (This data is being parsed from an external API).
I am currently changing the link textContent for each link, and appending the integer to the end of the textContent, which works well for all text links, however I am having an issue with adding this click count integer to the images on the page. What would be the best way to display the click count for images on the page. Would I be able to add the click count as an overlay on the image?
My code so far:
function ls(url) {
var getURL = "url" + url;
var req = new XMLHttpRequest();
req.open("GET", getURL, "true");
req.onload = function() {
var resObj = JSON.parse(req.responseText);
var links = document.querySelectorAll("a");
for (var i = 0; i < links.length; i++) {
var rawLink = links[i].href; var linkText = links[i].textContent; var link = links[i].href.replace(/(.*)#?/, "$1");
var escapedLink = escape(rawLink);
if (rawLink in resObj) {
links[i].textContent = linkText + " (" + resObj[rawLink] + ")";
} else if (escapedLink in resObj) {
links[i].textContent = linkText + " (" + resObj[escapedLink] + ")";
}
}
};
This just appends the link count to the end of the textContent, how can I add this linkcount to images without messing up the page layout.
One solution would be to wrap the images in a <div> and add the click count text as another child. Something like:
<div class="image-wrapper">
<img src="..." alt="..." />
<div class="click-count">10</div>
</div>
Then just style the click count text to be placed in a corner of the image using CSS. Possibly something like:
.image-wrapper { position: relative; }
.click-count { position: absolute; bottom: 10px; right: 10px; }
To add the text to the image, you would have to do something like this (note that I am using jQuery for simplicity and because the question is tagged as such):
$( 'img' ).each( function() {
// Test we need to add the click count
...
// Add the click count number
$( this ).wrap( '<div class="image-wrapper" />' ).append( '<div class="click-count">' + click_count + '</div>' );
});