javascript, creating <div> and <button> then clicking on that button - javascript

I've been given a widget that sales tickets to specific boat tour,
it needs
<div id="wt_piersWidget" data-api-key="1234" data-tour-id="12"> </div> container
and some <button class="wt-open"></button> ,
after click on the button it opens form to choose data and pier ro sale from. It works fine on the sites with separate pages for each tour. simple html for those pages is:
`<script type="text/javascript" src="/jquery/1.7.1/jquery.min.js"></script>
<link href="/wt_widget/style.css" rel="stylesheet">
<div id="wt_piersWidget" class="wt__start-hide"
data-api-key="1234"
data-program-id="12"
></div>
<button class="wt-open"></button>
<script src="/wt_widget/widget.js"></script>
`
I have some trouble implementing it on the site with special "sales" blocks for 3 different tours on a single page.
I am trying to check what exact button out of 3 possible is clicked, create <div> with attribute values that I need for this tour, then create <button> with correct class and click on it. concole.log() returns correct objects in console, but widget do not recieves any of those attriubute values.
I realise, that at this point I am misunderstanding core principles of how all this code works on the pages, I just do not know where to start to fix it.
<!-- css for widget -->
<link href="wt_widget/style.css" rel="stylesheet">
<!---------------------->
<div id="prices">
<div class="section">
<div class="container-fluid">
<h2 class="section-title">Prices block</h2>
<p class="section-subtitle">
</p>
<div class="row">
<div class="col-lg-3 col-md-6 col-sm-6">
<!-- One of the block with price list for one of the tours (for ex. tour#13) -->
<div class="panel panel__mm-ct">
<div class="cell">
<div class="panel-heading">
<div class="price-icon"
style="background-image: url('/site/assets/files/1111/icon_boat_bus.png');"></div>
<h3>tour title</h3>
<p>ticket info</p>
</div>
<div class="panel-body" id="">
<ul class="list-unstyled">
<li><i class="fa fa-ticket"
aria-hidden="true"></i>ticket type 1</li>
<li><i class="fa fa-ticket"
aria-hidden="true"></i>ticket type2</li>
</ul>
<button id="2" class="btn btn-success" onclick="setProgrammParam(this.id)">Order ticket</button> <!--button inside one o those 3 blocks, button id is how I separate one block from another, it does work -->
<div id="wt_piersWidget" class="wt__start-hide" data-api-key="123"></div> <!--widget template-->
<script>
function setProgrammParam(idCheck) { //idCheck stores value from sales block< so I know which one out of 3 tour blocks is clicked.
var vidgetDiv = document.getElementById("wt_piersWidget");
var attApi = document.createAttribute("data-api-key");
var attId = document.createAttribute("data-tour-id");//creating attributes I need
switch(idCheck) {// trying to set data-tour-id to correc tour, for ex. 12
case '1':
attId.value="12";
vidgetDiv.setAttributeNode(attId);
console.log(vidgetDiv);
var btn = document.createElement("BUTTON"); //creating <button> element I need
btn.setAttribute("id", "widgetButton1"); //with id so I could find it later
btn.setAttribute("class", "wt-open"); //with class widget needs this button to have
document.body.appendChild(btn);
console.log(btn);
document.getElementById('widgetButton1').click(); // and clicking on that button
break;
case '2':
attId.value="13";
vidgetDiv.setAttributeNode(attId);
console.log(vidgetDiv);
var btn = document.createElement("BUTTON"); //creating <button> element I need
btn.setAttribute("id", "widgetButton2"); //with id so I could find it later
btn.setAttribute("class", "wt-open"); //with class widget needs this button to have
document.body.appendChild(btn);
console.log(btn);
document.getElementById('widgetButton2').click(); // and clicking on that button
break;
default:
attId.value="10";
vidgetDiv.setAttributeNode(attId);
console.log(vidgetDiv);
var btn = document.createElement("BUTTON"); //creating <button> element I need
btn.setAttribute("id", "widgetButton0"); //with id so I could find it later
btn.setAttribute("class", "wt-open"); //with class widget needs this button to have
document.body.appendChild(btn);
console.log(btn);
document.getElementById('widgetButton0').click(); // and clicking on that button
break;
</script>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- jquery 1.7.1-->
<script type="text/javascript" src="1.7.1/jquery.min.js"></script>
<!--------------->
<!-- link to a widget-->
<script src="/wt_widget/widget.js"></script>
<!--/link to a widget-->
I posted here only one block, but they are basicly same and go one after another.
I take the trouble of generating those div's on-the-fly, because just copypasting symple html code with different div attributes 3 times results in all buttons working only with attributes from first div, which is how it is supposed to happen, that I understand. I am just having trouble understanding why there is any difference to a widget in the example above. From my understanding, I am generating all I need only after I pressed first button, and it generates only 1 div with attributes for a tour, then a single button pressed, widget should not see difference between this and simple html example, as I see it. Initially I tried to change value of already existing , but it still opened widget with default values.

In your first code block everything makes sense, a button is defined in your html and then js widget script file runs, attaching click event. But when you do it by code, something needs to attach those click handlers. I mean, the script ran before those buttons where created.

Related

I have a dynamic html template. Which forms when I click on a button. Now, the problem is When I click on one button all buttons are clicked

Here is the html template forms when I click on a button.
<div class="detail">
<h3>${element.name}</h3>
<p>Phone: ${element.phone}</p>
<button onclick="addToHistory()">Call</button>
</div>`
Let's say it creates two template as it is.
<div class="detail">
<h3>Person1</h3>
<p>Phone: 0111111111</p>
<button onclick="addToHistory()">Call</button>
</div>`
<div class="detail">
<h3>Person2</h3>
<p>Phone: 0111111111</p>
<button onclick="addToHistory()">Call</button>
</div>`
Now, I want to click on one button and then according to my click I want the data of clicked divs should store.
I tried this using event handler as you can see with addToHistory() function. But, it stores all the data both I clicked and not clicked also.
Please note: I want to do this only using JavaScript. Thank you for your support.
You can pass this into your method and use that to traverse within the specific detail container where the button was clicked.
this will reference the specific element the event occurs on
function addToHistory(el){
console.log(el.previousElementSibling.textContent);
}
<div class="detail">
<h3>Person1</h3>
<p>Phone: 0999999999</p>
<button onclick="addToHistory(this)">Call</button>
</div>`
<div class="detail">
<h3>Person2</h3>
<p>Phone: 0111111111</p>
<button onclick="addToHistory(this)">Call</button>
</div>`

Google Analytics - Get nested span event click without class and id

I am using Tag manager and Anayltics 360.
My code as follows,
<div rel="ABC_Links" class="ak_widget" >
<!-- BEGIN: Widget - Links -->
<section class="mfb-30">
<div class="widget_links">
<div class="widget_container">
<div class="widget_content">
<button type="button" class="buttonShadow" onclick="window.open('https://somepagelink.aspx); return false;">
<div class="widget_item">
<div class="widget_icon">
<svg>123</svg>
</div>
<div class="widget_text"><span style="overflow-wrap: normal;">ABCD TEXT</span></div>
</div>
</button>
<button type="button" class="buttonShadow" onclick="window.open('https://somepagelink.aspx); return false;">
<div class="widget_item">
<div class="widget_icon">
<svg> 12345</svg>
</div>
<div class="widget_text"><span style="overflow-wrap: normal;">XYZ TEXT</span></div>
</div>
</button>
</div>
</div>
</div>
</section>
<!-- END: Widget Links --></div>
I have 12 buttons in this same format. Here i have given an example of two buttons.
Button name i can change later so i can not take it as hard coded for "Click Text" in tag manager.
I can use only rel= "ABC_Links" as unique identifier. I can not use any of below class as they are not unique.
I have used Custome javascript to get parent child relationship but didn't work.
I have used DOM element variable but it did not work.
Now Question is, Is there any way to trigger event in tag manager when i click on any of the button below and get the info in real time event in Anayltics 360 ???
One way to achieve this would be to create a User-Defined Custom JavaScript variable in GTM to set isABCLink=true on button click.
On the Variables screen under Built-In Variables make sure you have "Click Element" ticked.
Create a User-Defined Variable
Name: isABCLink
Type: Custom JavaScript
Code:
function() {
return {{Click Element}}.matches("div[rel=ABC_Links] button, div[rel=ABC_Links] button *");
}
Create a Trigger
Trigger Type: Click - All Elements
This trigger fires on: Some Clicks
Conditions: isABCLink equals true
Set up your tag firing on above trigger
Once caveat to point out is that the exact element clicked on could be the button or one of the child elements of the button such as the <svg> which might make it hard to set up your tag depending on what exactly you need.

Preload HTML from within Javascript file, into the DOM, but don't execute

Im creating a simple JS game, which will run in the browser and will pull the files directly from the pc. Because of this needed to make a workaround the "Cross-origin" error, which went smoothly, I put all the HTML within a JS and loaded the JS from the tag in the index.html. Everything was going smoothly so far.
You click "press any key to continue" -> but then next you click "New Game" and you get event listener error "can't set property of null". Which i don't understand how it happens, because the element is already in the "DOM" , the css could pick it up, but the event listener could not? So i tried moving all scripts on top, rearranging stuff, nothing worked. Pre-loading all HTML into the index.html, without executing it will definitely work, but how can i achieve this exactly? My Configuration atm is:
index.html (root folder)
/components/main_menu.js
/components/character_creation.js
function MainMenu()
{/*
<div id="mm_wrapper_grid">
<div id="mm_subgrid1">
</div>
<div id="mm_subgrid2">
<div id="mm_subgrid2_image"></div>
<div id="mm_new_game"><p id="mm_new_game_p" class="grow">New Game<p></div>
<div id="mm_load_game"><p id="mm_load_game_p" class="grow">Load Game</p></div>
<div id="mm_options"><p id="mm_options_p" class="grow">Options</p></div>
<div id="mm_credits"><p id="mm_credits_p" class="grow">Credits</p></div>
</div>
</div>
*/}
function CharacterCreation()
{/*
<div> Character Creation test
</div>
*/}
<div id ="game">
<div id="loading_screen">Press Any Key To Continue</div>
</div>
<script type="text/javascript">
document.getElementById("loading_screen").addEventListener("click", function() {callback("game", MainMenu)});
document.getElementById("mm_new_game_p").addEventListener("click", function() {callback("game", CharacterCreation)});
function callback(arg1, func)
{
var html = func.toString();
var htmlstart = html.indexOf('/*');
var htmlend = html.lastIndexOf('*/');
var html = html.substr(htmlstart+2, htmlend-htmlstart-2);
document.getElementById(arg1).innerHTML = html;
}
</script>
You have the problem because you are trying to add the second listener for getElementById("mm_new_game_p") when this element doesn't exist on the page. I suggest you to add the follwing line of code to make sure, that it's true:
<script type="text/javascript">
document.getElementById("loading_screen").addEventListener("click", function() {callback("game", MainMenu)});
console.log('#mm_new_game_p', document.getElementById("mm_new_game_p"));
document.getElementById("mm_new_game_p").addEventListener("click", function() {callback("game", CharacterCreation)});
...
You have to add this listener after this element will be created (after calling of MainMenu() function).
To tell the truth, I can see such storing of html templates in js functions for the first time. It's hard to support your code and I've modified it a little bit below. This is not the best way to implement it, but my goal is show you a solution based on your code. What you can see here:
all the templates for pages are in html (not in JS);
you can add listeners from html code using HTML attribute onclick - and in this case you can be sure, that element exists on the page.
If you want to add listeners from JS, than you have to create own functions for each pages which you have and after HTML template of a page will be added to #game container you will need to create event listeners for the buttons which are on the created page.
openPage("game", "loading_screen");
function openPage(parentDOMElementId, name) {
console.log('openPage:', name);
var pageHtml = document.getElementById("page_" + name).outerHTML;
document.getElementById(parentDOMElementId).innerHTML = pageHtml;
}
<div id="game"></div>
<div class="templates" style="visibility: hidden;">
<div id="page_loading_screen" onclick="openPage('game', 'main_menu')">
Press Any Key To Continue
</div>
<div id="page_main_menu">
<div id="mm_subgrid1">
</div>
<div id="mm_subgrid2">
<div id="mm_subgrid2_image"></div>
<div id="mm_new_game">
<p id="mm_new_game_p" class="grow" onclick="openPage('game', 'character_creation')">New Game
<p>
</div>
<div id="mm_load_game">
<p id="mm_load_game_p" class="grow">Load Game</p>
</div>
<div id="mm_options">
<p id="mm_options_p" class="grow">Options</p>
</div>
<div id="mm_credits">
<p id="mm_credits_p" class="grow">Credits</p>
</div>
</div>
</div>
<div id="page_character_creation">
Character Creation test
</div>
</div>

Having trouble getting .siblings() to work in jQuery

I have a form on my site that allows people to create a document containing Sections, each of which contains Steps. Basically, the problem is that I can create Sections just fine, but Steps are not being created for some reason.
The HTML for each Section and each Step is created with underscore templates, which you can see in the two scripts below:
<script id="tmpl-section" type="text/html">
<div class="section">
<div class="form-group row">
// inputs
</div>
<div class="form-group row">
// more inputs
</div>
<a href="#" onclick="addSection($(this), true)">
Add section after
</a>
<a href="#" onclick="addSection($(this), false)">
Add section before
</a>
<h5>Steps</h5>
<div class="step-body"></div>
<hr />
</div>
</script>
<script id="tmpl-step" type="text/html">
<div class="step">
<div class="form-group row">
// inputs
</div>
<div class="form-group row">
// inputs
</div>
</div>
</script>
When someone clicks "Add section (before or after)", the following function is called:
function addSection(element, after) {
var sectionStructure = _.template($('#tmpl-section').html()),
$sectionBody = $('.section-body');
if ($('.section').length) {
if (after) {
element.closest('.section').after(sectionStructure);
} else {
element.closest('.section').before(sectionStructure);
}
} else {
$sectionBody.append(sectionStructure);
}
addStep($(this), true); // '$(this)' should refer to whichever "add section" link was clicked
}
($sectionBody refers to the part of the page that contains all the sections.) Basically, it's checking to see if there are any other sections on the page yet for when the page is first loaded, and if there aren't any, it adds one. If there are other sections, it adds another before or after whatever section was clicked. Not super relevant, but I wanted to explain the if statements in there.
Every time addSection() is called, it also calls another function called addStep() to initialize each new section with one step.
function addStep(element, after) {
var stepStructure = _.template($('#tmpl-step').html()),
$thisStepBody = element.siblings('.step-body');
$thisStepBody.append(stepStructure);
}
Eventually I will add a link in each Step to add another step before/after like each Section has, but I'm not that far yet.
The problem is, $thisStepBody.append(stepStructure); is not doing anything. My guess is that $thisStepBody.siblings('.step-body'), which is supposed to point to the ('.step-body') inside whichever section was clicked, is the problem. I've tried this a ton of different ways, and I don't know why it isn't working.
It seems like it should be a simple thing to do, but I'm worried the rest of what I'm trying to do is so overly complicated that it's messing up in a way I can't even think of.
addStep($(this), true);
should be
addStep(element, true);
The addSection() function isn't bound to the element that's clicked, the element is being passed as a parameter.

Javascript modal that displays list that closes and returns to main html

Rather new to javascript, jquery and bootstrap, etc., so bear with me. I have a situation where I want to present a list of errors in a model dialog after the user hits a "validate" button. Got all the working - I am generating a list of objects that indicate to the user they need more work to the exact spot that needs additional data entry. I have the the DIV "id" that represents the field that needs more data (and each item will jump someplace different).I do not want a drop down list since there are be lots and lots of these items.
A few questions:
How do I go about jumping from the modal to the main html. I believe I have seen scrollIntoView mentioned in a few other posts as I was looking but will that hop to the DIV and also close the modal?
What construct should I use for the list? A list of scrolling button? The size of this can be quite large (hundreds) so it will need a scroll capability.
Finally, the app is "paged" with a next and prev buttons. I assume that will not be a problem from the aspect of jumping to a page not already displayed?
Here is the current modal code:
<script id="template-validation-error" type="text/x-handlebars-template">
<div id="validationErrorModal" class="modal">
<div class="message-container">
<div class="header">
Validation Errors
</div>
<div class="message">
The following fields are required:
</div>
<div class="center">
<input type="button" class="btn btn-solid-green btn-sm" onclick="fffdevice.validationErrorOk();" value="Done" />
</div>
</div>
</div>
</script>
and
showValidationError: function (fieldlist) {
settings.focusedField = $(':focus');
$("#validationErrorModal").detach();
$(".device-container").append(templates.validationerror({ fieldlist }));
$(".message-container input").focus();
},
validationErrorOk: function () {
$("#validationErrorModal").detach();
if (settings.focusedField) {
settings.focusedField.focus();
}
},
The field list is a list of objects that contain the id (field.id) of the DIV and also a description (field.fieldName) that I want to display.
Here is something I mocked up in paint...I am not sold on it but it show in a general sense what I am looking for:
I don't need a full solution rather, just want mechanisms I can use.
UPDATE
Just to help out anyone else in the future, using the info provided in the correct answer below I have a new code as follows:
<script id="template-validation-error" type="text/x-handlebars-template">
<div id="validationErrorModal" class="modal">
<div class="validation-container">
<div class="header" align="center">
Validation Errors
</div>
<div class="message">
<div class="scrolling-container" style="background-color: rgb(238, 238, 238); height:660px">
<div class="grid grid-pad">
{{#each fieldlist}}
<div class="row click-row" onclick="fffdevice.validationErrorFix('{{id}}');">
<div class="col-7-8 field-name">{{fieldName}}</div>
<div class="col-1-8">
<img class="pull-right" src="/mysite/Content/device/images/fix.png" style="width: 40px; position:relative; top: -5px;">
</div>
</div>
{{/each}}
</div>
</div>
</div>
<div><br/></div>
<div class="center">
<input type="button" class="btn btn-solid-green btn-sm" onclick="fffdevice.validationErrorOk();" value="Done" />
</div>
</div>
</div>
Then the Javascript for the onClick is:
validationErrorFix: function (id) {
$("#validationErrorModal").detach();
var x = document.getElementById(id);
x.scrollIntoView({
behavior: "smooth", // or "auto" or "instant"
block: "start" // or "end"
});
},
Which closes the dialog and jumps to the field. It looks like (I know this is ugly and I will clean it up later):
Bind the modal event to the validation code and show the modal if error(s) are found.
Display the modal with the list of errors using an html unordered list, inside the li element an anchor tag where the href attribute will have a value with the id that corresponds to the input field, all this done dynamically from your validation code.
Once an error in the list is clicked hide the modal using bootstrap $('#your-error-modal').modal('hide'); so the code would be something like this:
$('#your-error-modal').on('click', 'a.error-item', function(){
$('#your-error-modal').modal('hide');
});
I haven't tested this code, but if you're having issues with scrolling to the section of the input and closing the modal you can probably do something like this too:
$('#your-error-modal').on('click', 'a.error-item', function(e){ // use this method of onclick because your list will be created dynamically
e.preventDefault(); // prevent the default anchor tag action
var href = $(this).attr('href'); // grab the href value
$('#your-error-modal').modal('hide'); // close the modal first
scrollToDiv(href); // then take the user to the div with error with a nice smooth scroll animation
});
function scrollToDiv(location) {
$('html, body').animate({
scrollTop: $(location).offset().top
}, 2000);
}
Again this is untested code, but the idea is there.
For UX reasons you might also want to create a floating div or something where users can click on it and go back to the modal to continue reading your list of errors.

Categories

Resources