AngularJs - $watch feature is only functional when Chrome Developer Tools are active

My goal is to create a $watch function within a directive that monitors changes in the height and width of an element. This will allow the element to be centered on top of an image using the height and width values.

Below is my app.js code:

app.directive('centerElement', function ($timeout) {
return function (scope, element, attrs) {
        scope.$watch(function () {
            return { 
                'h': element[0].offsetHeight, 
                'w': element[0].offsetWidth 
            };
        }, 
        function (newValue, oldValue) {
            var elementHeight = newValue.h;
            var elementWidth = newValue.w;

            /*To account for rounding errors and not loop*/
            if(elementHeight % 2 === 0)
            {
                element.css('margin-top', '-' + ((elementHeight) / 2) + 'px');
            }
            else 
            {
                element.css('margin-top', '-' + ((elementHeight + 1) / 2) + 'px');
            }
            if(elementWidth % 2 === 0)
            {
                element.css('margin-left', '-' + ((elementWidth) / 2) + 'px');
            }
            else
            {
                element.css('margin-left', '-' + ((elementWidth + 1) / 2) + 'px');
            }
        }, true);

        element.bind('centerElement', function () {
            scope.$apply();
        });
};
});

Here is my html code:

<div class="row relativePosition">
    <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 noPadding">
        <img id="getStartedGiveGiftImage" ng-src="{{homeCtrl.getHomeImageBySectionName('getStartedGiveGift').url}}"/>
    </div>
    <div id="getStartedGiveGiftCol" class="absolutePosition" center-element>
        <div class="col-lg-8 col-md-8 col-sm-8 col-xs-12">
            <div class="getStartedGiveGiftText headingTitle">
                Swapping, Sharing,
                <div class="getStartedGiveGiftText text-right">
                    and Caring
                </div>
            </div>
        </div>
        <div id="getStartedGiveGiftButtons" class="col-lg-4 col-md-4 col-sm-4 col-xs-12 text-right">
            <div>
                <a id="getStartedGiveGiftButton1" class="btn btn-default text-right redBackgroundGradient" href="{{homeCtrl.getPage('Get Started').route}}">Get Started</a>
            </div>
            <div>
                <a id="getStartedGiveGiftButton2" class="btn btn-default getStartedGiveGiftButton greenBackgroundGradient text-right text-white" href="{{homeCtrl.getPage('Give a Gift').route}}">Give a Gift</a>
            </div>
        </div>
    </div>
</div>

I have already set the CSS for the .getStartedGiveGiftCol to:

#getStartedGiveGiftCol {
top: 50%;
left: 50%; }

By applying margin-top and margin-left values that are half the height and half the width, the element will be perfectly centered over the image.

During testing in Google Chrome, I noticed that the centerElement directive did not trigger when the image loaded and the element's dimensions changed, unless I stepped through the code in Developer Tools. No errors occurred when running the app without Developer Tools open. Similar behavior was observed in Internet Explorer and Firefox, but Edge successfully centered the elements.

Although I am hesitant to use $watch due to potential performance issues, I have not found an alternative solution in the AngularJS documentation or online searches. I have also tried waiting for the image to load before centering the element, but no luck so far. Since this directive will be used in other parts of the app, I am eager to explore alternative methods to achieve this without using $watch.

As a newcomer to AngularJS, any assistance is greatly appreciated. If more information is needed, please let me know.

Thank you!

EDIT: To address the issue, I created another directive for the image that triggers scope.$digest() upon loading. While this improved the situation, I still encountered occasional failures after refreshing the page multiple times. Any suggestions?

Below is the directive I implemented:

app.directive('imageOnload', function() {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            element.bind('load', function() {
                scope.$digest();
            });
            element.bind('error', function(){
                alert('image could not be loaded');
            });
        }
    };
});

Answer №1

I managed to solve this issue independently. To achieve the desired outcome, I realized that I needed to include $timeout(); in my centerElement directive. This action triggers a $scope.$apply(); once the current $digest cycle is completed, ensuring that the changes made within the $watch are effectively applied to the page.

Below is my directive implementation:

app.directive('centerElement', function ($timeout) {
return function (scope, element, attrs) {
        scope.$watch(function () {
            $timeout();
            return { 
                'h': element[0].offsetHeight, 
                'w': element[0].offsetWidth 
            };
        }, 
        function (newValue, oldValue) {
            var elementHeight = newValue.h;
            var elementWidth = newValue.w;

            /*To account for rounding errors and prevent looping*/
            if(elementHeight % 2 === 0)
            {
                element.css('margin-top', '-' + ((elementHeight) / 2) + 'px');
            }
            else 
            {
                element.css('margin-top', '-' + ((elementHeight + 1) / 2) + 'px');
            }
            if(elementWidth % 2 === 0)
            {
                element.css('margin-left', '-' + ((elementWidth) / 2) + 'px');
            }
            else
            {
                element.css('margin-left', '-' + ((elementWidth + 1) / 2) + 'px');
            }
        }, true);

        element.bind('centerElement', function () {
            scope.$apply();
        });
};
});

If anyone has suggestions on how to eliminate the use of $watch, please share. I am unable to rely solely on CSS due to the position: relative attribute of the row and the position: absolute attribute of the element. Thus, I must utilize top: 50%, left: 50%, negative margin-top set to half the element's height, and negative margin-left set to half the width (e.g., margin-top: -100px for a 200px height element and margin-left: -50px for a 100px width element) in order to center the element on the image. Determining the width and height of the element dynamically, as opposed to hard-coding it in CSS, is essential for future maintenance, especially across various screen sizes in bootstrap. Therefore, I am resorting to using $watch. Additionally, I am utilizing bootstrap, eliminating the option of CSS3 flexbox due to its impact on bootstrap grid responsiveness.

If you have any alternative approaches to replace the $watch, kindly advise.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Guide on accessing and loading HLS stream via JSON API in an Ionic 1 application

I am attempting to stream a video using HLS from a JSON API in my Ionic app. Surprisingly, I haven't encountered any errors thus far. Here's what I've tried so far, but still can't seem to get it to work: <div ng-repeat="item in ...

Deciphering JSON strings using JavaScript

Here is a string that I am trying to parse using Json: {\"description\": \"PSY - Gangnam Style (\\uac15\\ub0a8\\uc2a4\\ud0c0\\uc77c) \\n\\u25b6 NOW available on iTunes: h ...

MaterialUI is not displaying input styling correctly

I'm a beginner with MaterialUI and I seem to be missing something simple. This is the code I am trying to work with: <Container> <FormControl> <FormGroup> <InputLabel htmlFor="email">Email addre ...

Is it necessary for the keys in separate loops to be unique among siblings?

Does the use of key in loops create separate branches, or do they still need to be unique for the same parent? Take a look at this example: // This is obviously an error: <div> <div key="gimme-a-break" /> <div key="gim ...

Error occurs when attempting to reference an object from an NPM package

Currently, I'm attempting to utilize the https://github.com/iamcal/js-emoji library for colon-to-emoji conversion. Following the installation of its NPM package, I included <script src="../node_modules/emoji-js/lib/emoji.js" type="te ...

Can Vue.js be configured to reload specific components only?

Can a specific component be reloaded in a Vue component that contains multiple components? For example, if there is a component structured like this: Main component <template> <button>Button<button> <component1></component> ...

Setting MenuItem to the correct width in asp.net simplified

I have a 1000px wide container, within it there is a menu control with 5 items (links). Each item has a width of 200px in the CSS file to make use of the entire length of the line. .level1 { border-width: 0px; margin: 0px; padding: 0px; background ...

Javascript Pretty Print Format is producing inaccurate output

function displayData(info) { document.body.appendChild(document.createElement('pre')).innerHTML = info; } function searchInGitHub(str) { const http = new XMLHttpRequest(); http.open("GET", "https://api.github.com/search/reposi ...

What is the process for linking my website hosted on an external server to the database running on XAMPP on my local machine?

Currently, I have been working on a project website using XAMPP. The site includes a database where users can register and store their information. Initially, my plan was to test the website with friends by physically giving them my laptop. Unfortunately, ...

Transforming the add to cart button into a view button within the product listings: an easy guide

I am currently developing a unique ecommerce website called bookslab.in. To enhance the user experience, I would like to replace the "add to cart" button with a "view details" button when users view the list of available products. Furthermore, when users c ...

Maintain saved states upon page load/refresh using jQuery Cookies

I'm currently working on enhancing the accessibility features of a website. To achieve this, I have integrated three toggle buttons - one for adjusting font size, another one for highlighting links, and the third one for inverting colors. My objective ...

Ensuring the authenticity of dynamic forms

jQuery(document).ready(function(){ $("#submitButton").click(function () { if ( $("#formToSubmit").validationEngine('validate') == true) { $("#formToSubmit").submit(); } }); Utilizing the Validation Engine plugin for jQuery to valida ...

ngAnimateSwap - animations do not function as intended when boolean expressions are utilized

I adapted the original ngAnimateSwap demonstration from the AngularJS documentation to utilize a boolean expression for triggering the slide animation. Initially, I anticipated the banner to switch back and forth between 'true' and 'false&a ...

Create a React component that can be rendered multiple times without duplicating

I have a component that must render a specific item based on the props it receives from its parent component. const Contract = ({ savingsFactors, isContract }) => ( {isContract ? ( <PutField label={label} placeholder={plac ...

Issue: Troubile in fetching CSS file from CDN and using local copy as fallback, What is the solution?

I previously inquired about this issue, however, I did not receive a satisfactory solution. Therefore, here I am asking again for your assistance. I am attempting to retrieve my CSS from an external CDN service, such as http://cdn.example.com/. This scrip ...

How can I dynamically update the sidebar in Ionic 3 post-login without the need to reload the app or refresh the browser?

I have successfully implemented login and sign up functionality in my ionic 3 app. However, I am facing an issue where the username is not updating in the sidebar instantly after logging in. Currently, I need to refresh the browser or close and reopen the ...

Dealing with null exceptions in Angular 4: Best practices

Hi there, I am encountering an issue with binding my model data to HTML fields where when I try to edit the data it returns an error saying "cannot read value of null". How can I resolve this? Here is the HTML code snippet: <div class="form-group"> ...

Use JavaScript to dynamically generate a drop-down select menu

Is there a way to automatically expand a select menu dropdown using JavaScript or jQuery when a button is clicked? I am facing this challenge because I have a text field that allows users to input custom fields dynamically into a select input. My goal is ...

jQuery code runs smoothly on Firefox but encounters issues on Chrome

I'm experiencing an issue with a script that is supposed to post a comment and load the answer from a server. The script works fine in Firefox, but in Chrome, it seems that no event is triggered. You can click the button, but nothing happens. I'v ...

Managing failure function in AJAX: A comprehensive guide

I've been trying to manage the failure function in AJAX to display specific messages to users. Despite attempting various solutions, I have struggled to control the failure function. Here is my code: <p id="Likes" class="alert-danger" style="displ ...