mouseover and mouseout events not firing using pure js - javascript

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

Related

Adding and using html with jquery works only sometimes

I wrote some code that generates a bunch of html-elements based on a Json-Object. I add it to the page by JQuerys .append() and .after().
It does work perfectly often, but sometimes the outer loop is only executed once and stops at $( '#'+inputname ).entityselector().
function addlinks(qid, prop) {
html="<fieldset id=\"quickpresets\">" +
"<legend>Quick Presets (" + prop.name + ")</legend></fieldset>";
$('.wikibase-statementgrouplistview').first().after( html );
for( var p = 0; p < prop.defaults.length; p++ ) {
pid=prop.defaults[p].pid;
pname=prop.defaults[p].name;
pvalues=prop.defaults[p].values;
inputname="input"+pname;
pclass="addstatement";
if($('#P'+pid).find(".wikibase-snakview-value a").length !== 0) {
pclass += " disabled";
}
str="<p class='"+pclass+"'>Add "+pname+":";
for( var i = 0; i < pvalues.length; i++) {
toqid=pvalues[i].qid;
toname=pvalues[i].name;
str += " <a href='javascript:void(0);' onclick=\""+
"additemstatement("+qid+","+pid+",'"+pname+"',"+ toqid +",'" + toname+ "')\">" + toname+ "</a>"+
" ∙";
}
str += "<span class=\"quickpresetsinput\"><input id='"+inputname+"'/> ";
str += "<a href=\'javascript:void(0);\' onclick=\""+
"onselectitem("+qid+","+pid+",'"+pname+"','"+ inputname +"')\">✔</a>";
str += "</span></p>";
$('#quickpresets').append( str );
input = $( '#'+inputname ).entityselector( {
url: 'https://www.wikidata.org/w/api.php',
language: mw.config.get('wgUserLanguage')
} );
}
}
How do I fix this issue? And what other things are there that I should do to improve this ugly code?
Updates:
I get the following error in the console:
TypeError: $(...).entityselector is not a function [Weitere
Informationen]
The full code can be found here.
I have to use ES5.
The data is always the same ("hard coded") JSON.
See below for the better readable version of Roamer-1888 – which still causes the same bug.
It's not apparent in the code why .entityselector() should throw on some occasions. The most likely reason is that you are trying to invoke the plugin before it is loaded.
In an attempt to fix the issue, you might try initialising all the inputs in one hit instead of individually in the loop.
Also, the code would be made more readable by attaching a fairly simple fragment to the DOM, then immediately adding links and attaching event handlers with jQuery.
Here it is the way I would write it (with a bunch of tidying up) :
function addlinks(qid, prop) {
// compose and intert fieldset.
var $fieldset = $("<fieldset><legend>Quick Presets (" + prop.name + ")</legend></fieldset>")
.insertAfter($('.wikibase-statementgrouplistview').first());
// loop through prop.defaults
prop.defaults.forEach(function(dflt) {
// compose and append a basic fragment ...
var $fragment = $("<p class='addstatement'>Add " + dflt.pname + ":<span class=\"links\"></span>"
+ "<span class=\"quickpresetsinput\"><input /> ✔</span></p>")
.appendTo($fieldset);
// ... then allow jQuery to augment the appended fragment :
// i) conditionally addClass('disabled')
if($('#P' + dflt.pid).find(".wikibase-snakview-value a").length !== 0) {
$fragment.addClass('disabled');
}
// ii) loop through dflt.values and add links.
dflt.values.forEach(function(val) {
$("<span> ∙</span>")
.appendTo($fragment.find('span.links'))
.find('a')
.text(val.name)
.on('click', function(e) {
e.preventDefault();
additemstatement(qid, dflt.pid, dflt.pname, val.qid, val.name);
});
});
// iii) attach click handlers to the quickpresets inputs
$fragment.find('.quickpresetsinput').find('a').on('click', function(e) {
e.preventDefault();
var selection = $(this).prev('input').data('entityselector').selectedEntity();
additemstatement(qid, dflt.pid, dflt.pname, selection.id.substring(1), selection.label);
});
});
// invoke .entityselector() on all the quickpresets inputs in one hit
$('.quickpresetsinput input').entityselector({
'url': 'https://www.wikidata.org/w/api.php',
'language': mw.config.get('wgUserLanguage')
});
}
untested except for syntax
That's certainly tidier though without a proper understanding of the original issue, .entityselector() may still throw.

How to create a toggle button that dynamically changes HTML content?

I have been working on this question for several days, and have researched it on SO as well as the web at large and was unable to find material that helped me solve it.
I am trying to create a weather app that can toggle the weather units displayed between Fahrenheit and Celsius. I start by appending the weather in Fahrenheit, and then I have created an event handler that conditionally changes the inner content of the associated element based on whether that element is currently displaying "F" or "C".
As it is, my app successfully loads with the Fahrenheit temperature, and toggles to Celsius on click, but it will not toggle back to Fahrenheit. I assume there is some issue with how the events are registered, but for the life of me I cannot figure it out.
Here is my code:
var fahr = document.createElement("a");
fahr.attr = ("href", "#");
fahr.className = "tempUnit";
fahr.innerHTML = tempf + "°F" + "<br/>";
$("#currentWeather").append(fahr);
var cels = document.createElement("a");
cels.attr = ("href", "#");
cels.className = "tempUnit";
cels.innerHTML = tempc + "°C" + "<br/>";
var units = document.getElementsByClassName("tempUnit");
$(".tempUnit").click(function() {
if (units[0].innerHTML.indexOf("F") != -1) {
$(".tempUnit").replaceWith(cels);
} else {
$(".tempUnit").replaceWith(fahr);
}
})
Thank you so much in advance! Happy to provide additional information if necessary.
Currently what you are using is called a direct binding which will only attach to element that exist on the page at the time your code makes the event binding call.
As you using replaceWith(), existing element is replaced with new element and event handlers are not attached with them.
You need to use Event Delegation using .on() delegated-events approach.
General Syntax
$(parentStaticContainer).on('event','selector',callback_function)
Example, Also use this i.e. current element context and use setAttribute() to update href element
$("#currentWeather").on("click", ".tempUnit", function() {
if (this.innerHTML.indexOf("F") != -1) {
$(this).replaceWith(cels);
}
else {
$(this).replaceWith(fahr);
}
})
var tempf = 212;
var tempc = 100;
var fahr = document.createElement("a");
fahr.setAttribute("href", "#");
fahr.className = "tempUnit";
fahr.innerHTML = tempf + "°F" + "<br/>";
$("#currentWeather").append(fahr);
var cels = document.createElement("a");
cels.setAttribute("href", "#");
cels.className = "tempUnit";
cels.innerHTML = tempc + "°C" + "<br/>";
$("#currentWeather").on("click", ".tempUnit", function() {
if (this.innerHTML.indexOf("F") != -1) {
$(this).replaceWith(cels);
} else {
$(this).replaceWith(fahr);
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="currentWeather"></div>

Write text over div without using .text()

I've got again a rather simple question, that I couldn't find an answer to.
I was using sofar the Jquery function .text() to write text on mouseenter on a dynamically created div. I came to realise that this only worked on my Iceweasel, but not in Chrome for instance. Instead ot .text() everywhere people advised of using the .val(), but I can't seem to figure out exactly how to use it in my implementation, since the divs had no previous text value.
Please find below a simple code, with .text() to understnad the question.
(function(){
for (var i = 0; i < 3; i++) {
var span = document.createElement("span");
span.innerHTML = "<img width=\"" + data.size[i][0] + "\" height=\"" + data.size[i][1] + "\" id=\"" + i + "\">";
span.style.position = "absolute";
span.style.left = data.coords[i][0] + "px";
span.style.top = data.coords[i][1] + "px";
document.body.appendChild(span);
}
}());
for (var i=0; i<3; i++) {
$('#' + i).mouseenter(function() {
$(this).text("text");
});
$('#' + i).mouseleave(function() {
$(this).text("")
});
}
http://jsfiddle.net/ckpx6esj/1/
I hope someone can give me an idea, of how to apply .val() or use something else entirely to make this work for chrome also.
Best Regards and Thanks in advance!
The problem is that you put text in an image tag!
<img>Some text</img>
This is invalid HTML, see this answer.
If you want text over an image, I suggest using a div with background: url(...) instead.
Updated fiddle.
The cleverest I could think to don't screw up your for loop is appending a <p> tag containing your text and removing it on mouseleave:
for (var i=0; i<3; i++){
$('#' + i).on("mouseenter",function() {
$(this).parent().append("<p>text</p>");
});
$('#' + i).on("mouseleave",function() {
$(this).parent().find("p").remove();
});
}
Fiddle:
http://jsfiddle.net/ckpx6esj/2/
Besides, text was not working because you are listening to the image (<img>) instead of the span. Images has no .text() prototype, hence you should access its parent() (which is a <span> in that case) if you want to use the .text() prototype, but using .text() on the parent will remove the image, hence the idea of appending the text and removing it later.
According to specification, val() function is to set value attribute and it only matters for input fields on your page. text() function is to change content of your element.
The .val() method is primarily used to get the values of form elements
such as input, select and textarea.
So you should use text() function in your code.
Also according to your code you change text property of <img> element. This is not good. You should change text of your <span>. So just move your id to span element.
If you want the jQuery equivalent of Javascript's native innerHtml, go for $(this).html('text');.
Take a look at these functions:
http://api.jquery.com/html/
$(this).html('text');
http://api.jquery.com/append/
$(this).append('text'); // Note that this appends instead of replaces
http://api.jquery.com/val/
$(this).val('text');
Or if you're feeling adventurous:
http://api.jquery.com/appendto/
$('text').appendTo($(this)); // Performance penalty for creating an object out of 'text'
First I will use class instead id, it will save using the second loop,
also if you want to have also text and also image you can do it but it will be littel complicated I would recommand add some child element to the span that will contain the text, I didnt do it just for the challenge
http://jsfiddle.net/ckpx6esj/5/
simple plugin to change the text without changing the html elements
$.fn.selectorText = function(text) {
var str = '';
this.contents().each(function() {
if (this.nodeType === 3) {
if(typeof(text) === 'string'){
this.textContent = text;
return false;
}else{
str += this.textContent || this.innerText || '';
}
}
});
return str;
};
var thisData = [{
'coords' : [[100,100], [300, 300], [200, 200]],
'size' : [[30, 30], [30, 30], [30, 30]]
}];
var data = thisData[0];
(function(){
for (var i = 0; i < 3; i ++){
var span = document.createElement("span");
span.setAttribute('class','spanImage');
span.style.position = "absolute";
span.style.left = data.coords[i][0] + "px";
span.style.top = data.coords[i][1] + "px";
span.innerHTML = "\n<img width=\"" + data.size[i][0] + "\" height=\"" + data.size[i][1] + "\" id=\"" + i + "\">";
document.body.appendChild(span);
}
$('.spanImage')
.on( 'mouseenter', function() {
$(this).selectorText('text');
})
.on( 'mouseleave', function() {
$(this).selectorText('');
});
}());

jquery file upload not triggering click in dynamically generated input

I am dynamically generating input divs and then trying to import the images to the div but the file upload is not working.
I have a image button for a file upload instead of choose files.
(function() {
var fileTriggers = $('.js_file_emit');
$('#row_container').on('click', '.js_file_emit', function() {
debugger;
console.log($(this).attr('data-file'));
$($(this).attr('data-file')).live('click', function(e) {
$('#' + $(this).attr('data-file')).on('change', function() {
handleFiles($(this).files, $(this).attr('data-preview'));
});
})
})
})();
the code is working if I have predefined inputs. http://jsfiddle.net/xnf0bht7/9/
This is how I am generating my div
function updatePopupRows(data) {
var l_sRowContainer = "#row_container",
l_sNameTemp = null,
l_sRowTemp = null,
l_sRowHeader = null,
l_sImgTemp = null,
l_sFileTemp = null,
l_sPrevTemp = null;
$(l_sRowContainer).html('');
for (var i = 0; i < data.data.length; i++) {
l_sRowTemp = document.createElement('div');
$(l_sRowTemp).attr('class', 'row');
l_sRowHeader = document.createElement('div');
$(l_sRowHeader).attr('class', 'row_header_fancybox');
l_sNameTemp = document.createElement('span');
$(l_sNameTemp).html(data.data[i].name);
l_sImgTemp = document.createElement('input');
$(l_sImgTemp).attr('type', 'image').attr('class', 'js_file_emit').attr('id', 'js_file_emit').attr('src', '/toogle/resources/js/slickgrid/add.png').attr('data-file', 'group' + data.data[i].name + i);
l_sFileTemp = document.createElement('input');
$(l_sFileTemp).attr('type', 'file').attr('multiple', 'true').attr('id', "group" + data.data[i].name + i).attr('data-preview', 'preview' + data.data[i].name + i);
l_sPrevTemp = document.createElement('div');
$(l_sPrevTemp).attr('id', 'preview' + data.data[i].name + i);
$(l_sRowHeader).append(l_sNameTemp).append(l_sImgTemp).append(l_sFileTemp).append(l_sPrevTemp);
$(l_sRowTemp).append(l_sRowHeader);
$(l_sRowContainer).append(l_sRowTemp);
}
}
HTML:
<div id='row_container'></div>
All click listeners should use the .on('click','selector',function(){}) syntax (you use the deprecated "live" method in your nested click). The click listeners can be loaded when the page loads. Anytime you add an element, also assign it the class that you use to set the click listeners.
You must include the preventDefault() call to keep the child click from bubbling up and firing the parent action. Here is an example of dynamically created elements with click listeners:
http://jsfiddle.net/iaretyler/qwn0a8k4/
Notice I have the click listener on the page already, I just create the element with the appropriate class. The same should be done with the "change" listener
You should also check out creating new elements with jQuery. It simplifies the creation a bit. You can change this:
l_sRowTemp =document.createElement('div');
$(l_sRowTemp).attr('class','row');
to this (notice the attributes go in the second param:
var l_sRowTemp = $('<div>', {class: "row"});
//Now append to whatever

Problems updating the content of a Kendo UI TabStrip without AJAX?

I have a small console that displayed the output of certain actions in my website, the need to have another console that would display a different kind of output made me want to combine both consoles in a Kendo UI TabStrip, the thing is that the information displayed on the console isn't received with AJAX so when I started to insert the HTML elements like before, the tab didn't got updated.
This is how I initialize the tab:
$('#tabConsole').kendoTabStrip({
animation: {
open: {
effects:'fadeIn'
}
}
});
This is how my HTML looks:
<div id="tabConsole">
<ul>
<li class="k-state-active">Salida</li>
<li id="notificacionTab">Notificaciones</li>
</ul>
<div id="userConsole"></div>
<div id="notificationConsole"></div>
</div>
This is how I try to update it:
function appendToConsole(content, type, optional) {
//code to append to console
var actualDate = new Date();
var prevContent = $('#userConsole').html();
if (typeof prevContent === 'undefined') {
prevContent = '';
}
var strType = '';
var iconType = '';
var moreOutput = '';
if (type == 1) {
strType = 'infoConsole';
iconType = 'infoIcon.png';
} else if (type == 2) {
strType = 'warningConsole';
iconType = 'warningIcon.png';
moreOutput = '<img id="viewDetails" value="' + optional + '" class="moreConsole" src="../Content/images/icons/arrow.png">';
} else if (type == 3) {
strType = 'errorConsole';
iconType = 'errorIcon.png';
}
var iconOutput = '<img class="iconConsole" src="../Content/images/icons/' + iconType + '">';
var dateOutput = '<div class="dateConsole">' + iconOutput + ' ' + actualDate.toLocaleDateString() + ', ' + actualDate.toLocaleTimeString() + ' : </div>';
var consoleOutput = prevContent + '<div class="outputConsole">' + dateOutput + content + moreOutput + '</div>';
$('#userConsole').html(consoleOutput.toString());
var height = $('#userConsole')[0].scrollHeight;
$('#userConsole').scrollTop(height);
//my try to update the tab
var tabStrip = $('#tabConsole'),
selectedIndex = tabStrip.data('kendoTabStrip').select().index(),
clone = original.clone(true);
clone.children('ul')
.children('li')
.eq(selectedIndex)
.addClass('k-state-active')
.end();
tabStrip.replaceWith(clone);
$('#tabConsole').kendoTabStrip({
animation: {
open: {
effects: 'fadeIn'
}
}
});
}
How can I update the contents of the DIV that are inside the TabStrip?
EDIT
It seems Kendo UI renames the DIVs' ids that represent the tabs to tabConsole-1 and tabConsole-2, explaining why the update wasn't working, still there's a lot of strange behavior, I had to specify the height for each DIV so the overflow propety would work, also the images with id viewDetails and class moreConsole when set to position absolute, get rendered outside the DIV that represents the tab, but if I change the position property to relative, the images stay inside the DIV but are displayed not at the end of the DIV like I want to, but relative to the size of the DIV that comes before them.
I'm really confused as to how to set the styles so the contents are displayed properly.
Adding content to a tabStrip can be achieved using:
$(tabConsole.contentElement(idx)).append(newContent)
where:
idx is the tab index,
newContent is what you want to add to the existing content and
tabConsole is the variable set to $("#...").kendoTabStrip(...).data("kendoTabStrip");.
You don't need to create a new tabStrip (in addition you should not re-create KendoUI elements since this is a pretty expensive operation).
About using multiple tags with the same id... you should not use it, use class instead. ids should be unique.
I'm still trying to understand your problem with the styling.

Categories